假设有一个 Java 类没有为它的所有字段提供 getter 和 setter,我必须用它们来扩展:gen-class
和摆弄它们。
如何访问超类字段?
我现在想到的最快(也许是最干净的......)解决方案是创建一个扩展我的超类的 java 类,并改为扩展它,但我想知道是否有一个听起来更直接的替代方案。
谢谢!
:exposes
生成的类中的方法可以在 的选项的帮助下访问基类字段gen-class
。:exposes
需要一个映射,其中键是匹配基类字段名称的符号;值也是像{:get getterName, :set setterName}
. Clojure 会自动生成这些 getter 和 setter 方法。它们可用于读取和修改基类字段。这记录在gen-class
.
这种方法适用于公共和受保护领域。它不适用于私有字段。
假设 Java 基类是这样的:
package fields;
class Base {
public String baseField = "base";
}
生成子类的 Clojure 代码将是:
(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))))
假设所有这些都是 AOT 编译并运行的,输出为:
base-sub
new-base-sub
我在理解所有细节时遇到了一些麻烦,因此决定尝试一个最小版本。这是一个文件列表:
> 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
在此处查看 AccessibleObject 的文档。请注意,反射期间返回的类Field
&Method
都包括在内。