扩展:gen-class暴露其裸露领域的类

时间:2017-06-29 08:06:38

标签: clojure gen-class

假设有一个Java类没有为其所有字段提供getter和setter,我必须使用:gen-class扩展它并使用它们。

如何访问超类字段?

现在我想到的最快(也许是最干净的......)解决方案就是创建一个扩展我的超类的java类,然后扩展它,但是我想知道是否有另外一个听起来更多直接

谢谢!

2 个答案:

答案 0 :(得分:3)

生成的类中的方法可以借助:exposes gen-class选项访问基类字段。 :exposes期望一个映射,其中键是与基类字段名匹配的符号;值也是{:get getterName, :set setterName}之类的地图。 Clojure自动生成那些getter和setter方法。它们可用于读取和修改基类字段。 gen-class package fields; class Base { public String baseField = "base"; } 中记录了这一点。

此方法适用于公共和受保护的字段。它不适用于私人领域。

假设Java基类如下:

(ns fields.core
  (:gen-class
   :extends fields.Base
   :methods [[bar [] String]
             [baz [String] Object]]
   :exposes { baseField { :get getField :set setField }}))

(defn -bar [this]
  (str (.getField this) "-sub"))

(defn -baz [this val]
  (.setField this val)
  this)

(defn -main
  [& args]
  (println (.. (fields.core.) (bar)))
  (println (.. (fields.core.) (baz "new-base") (bar))))

生成子类的Clojure代码是:

base-sub
new-base-sub

假设所有这些都是AOT编译并运行,输出为:

#include <stdio.h>   
#include <cmqc.h>   
#include <cmqxc.h>    
#include "dte_mq.h"    
#include <string.h>    
#include <stdlib.h>    

typedef struct tagDTE_QUEUE_DESCRIPTOR    
{    
     MQHOBJ handle;    
     int    IsSyncpointControled;    
} DTE_QUEUE_DESCRIPTOR, *PDTE_QUEUE_DESCRIPTOR;    

static MQHCONN sHConn = 0;    
static MQLONG  sCompCode = MQCC_OK;    
static MQLONG  sReason = MQRC_NONE;    
static int     sNumOpenQueues = 0;    
static PDTE_QUEUE_DESCRIPTOR sQueues = NULL;    
MQLONG   OpenCode;    
MQOD     od = {MQOD_DEFAULT};    /* Object Descriptor */    
MQMD     md = {MQMD_DEFAULT};    
MQPMO    pmo = {MQPMO_DEFAULT};    
MQLONG   O_options;    
MQGMO   gmo = {MQGMO_DEFAULT};    
/* MQCONNX options */    
MQCNO   Connect_options = {MQCNO_DEFAULT};    
/* Client connection channel */     
MQCD    ClientConn = {MQCD_CLIENT_CONN_DEFAULT};    



#define MAX_NUM_OPEN_QUEUES     10    


DTE_MQ_RESULT dteMqSend(int qd, void *buf, int len)    
{    
    printf("oleg\n");    
   /* memcpy(md.Format, MQFMT_STRING, MQ_FORMAT_LENGTH); */    
   md.MsgType  = MQMT_DATAGRAM;    
   printf("oleg1\n");    
   memcpy(md.MsgId, MQMI_NONE, sizeof(md.MsgId));    
   printf("oleg2\n");    
   memcpy(md.CorrelId, MQCI_NONE, sizeof(md.CorrelId));    
   printf("oleg3\n");    
   memcpy(md.Format, MQFMT_STRING, (size_t)MQ_FORMAT_LENGTH);    
   printf("oleg4\n");    
   printf("QD is = %d\n",qd);    
  if(sQueues[qd].IsSyncpointControled)    
      pmo.Options |= MQPMO_SYNCPOINT;    
   printf("oleg5\n");    


   MQPUT(sHConn, sQueues[qd].handle, &md, &pmo, len, buf, &sCompCode, &sReason);    

   printf("MQput CC=%ld RC=%ld\n", sCompCode, sReason);    
   if (sCompCode != MQCC_OK) return DTE_MQR_FAILED;    
   return DTE_MQR_OK;    
}    

答案 1 :(得分:1)

我在理解所有细节方面遇到了一些麻烦,并决定试用最小版本。这是一个文件列表:

> d **/*.{clj,java}
-rw-rw-r-- 1 alan alan 501 Jun 29 17:11 project.clj
-rw-rw-r-- 1 alan alan 431 Jun 29 17:10 src/demo/core.clj
-rw-rw-r-- 1 alan alan  63 Jun 29 16:57 src-java/jpak/Base.java

以下是project.clj

(defproject demo "0.1.0-SNAPSHOT"
  :description "demo code"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [
    [org.clojure/clojure "1.8.0"]
    [tupelo "0.9.55"]
  ]
  :profiles {:dev {:dependencies [ [org.clojure/test.check "0.9.0"] ] }
             :uberjar {:aot :all} }
  :java-source-paths ["src-java"]
  :aot [ demo.core ]
  :main ^:skip-aot demo.core
  :target-path "target/%s"
  jvm-opts ["-Xms500m" "-Xmx500m" ]
)

和Java类:

package jpak;
public class Base {
  public long answer = 41;
}

和我们的Clojure代码:

(ns demo.core
  (:gen-class
   :extends jpak.Base
   :exposes {answer {:get getans :set setans}}
 ))

(defn -main
  [& args]
  (let [sub-obj    (demo.core.) ; default name of subclass
        old-answer (.getans sub-obj)
        >>         (.setans sub-obj (inc old-answer))
        new-answer (.getans sub-obj)  ]
    (println "old answer = " old-answer)
    (println "new answer = " new-answer)
  ))

我们可以使用lein run来获取:

> lein run  
old answer =  41
new answer =  42

版本2

如果Java变量answer受到保护,则上述内容继续有效,但如果private或“包受保护”(无限定符)则失败。这是有道理的,因为我们的子类在不同的包中。

此外,如果我给子类一个不同于默认值的名称,这是一个更清洁的,这是一个clojure命名空间名称“demo.core”:

(ns demo.core
  (:gen-class
   :name demo.Sub
   :extends jpak.Base
   :exposes {answer {:get getans :set setans}}
 ))

(defn -main
  [& args]
  (let [sub-obj    (demo.Sub.) ; new name of subclass
        old-answer (.getans sub-obj)
        >>         (.setans sub-obj (inc old-answer))
        new-answer (.getans sub-obj)
  ]
    (println "old answer = " old-answer)
    (println "new answer = " new-answer)
  ))

版本3:访问private成员值

在Java中,子类通常不能看到超类的私有成员变量;来自不同包装的“包装保护”成员也受到限制。这是讨厌的Java类:

package jpak;
public class Base {
  private long answer = 41;
}

但是,Java具有覆盖private访问限制的众所周知的能力,您甚至不需要子类!您需要做的就是使用反射。这是clojure版本:

(ns demo.break
  (:import [jpak Base]))

(defn -main
  [& args]
  (let [base-obj   (Base.)
        class-obj  (.getClass base-obj)
        ans-field  (.getDeclaredField class-obj "answer")
        >>         (.setAccessible ans-field true)
        old-answer (.get ans-field base-obj)
        >>         (.set ans-field base-obj 42)
        new-answer (.get ans-field base-obj)
  ]
    (println "old answer = " old-answer)
    (println "new answer = " new-answer)))

> lein run -m demo.break
old answer =  41
new answer =  42

See the docs for AccessibleObject here。请注意Field&amp; Method,包括反射期间返回的类,都包括在内。