用 :gen-class 扩展一个 class 暴露它的裸字段
Extending with :gen-class a class that exposes its naked fields
假设有一个 Java class 没有为其所有字段提供 getter 和 setter,我必须用 :gen-class
和 fiddle 扩展它和他们在一起。
如何访问超级class 字段?
我现在想到的最快(也许是最干净...)的解决方案是创建一个 java class 来扩展我的超级 class,然后扩展它相反,但我想知道是否有其他听起来更直接的选择。
谢谢!
我在理解所有细节时遇到了一些困难,因此决定尝试一个最小版本。这是一个文件清单:
> 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 class:
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
或 "package protected"(无限定符)则失败。这是有道理的,因为我们的 subclass 在不同的包中。
此外,如果我给 subclass 一个不同于默认值的名称,即 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中,subclass通常看不到superclass的private成员变量; "package protected" 来自不同包的成员也受到限制。这是讨厌的 Java class:
package jpak;
public class Base {
private long answer = 41;
}
然而,Java 具有超越 private
访问限制的众所周知的能力,您甚至不需要子class!您需要做的就是使用反射。这是 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
和 Method
,它们是反射期间返回的 classes,都包括在内。
生成的 classes 中的方法可以在 gen-class
的 :exposes
选项的帮助下访问基础 class 字段。 :exposes
需要一个映射,其中键是与基础 class 字段名称匹配的符号;值也是像 {:get getterName, :set setterName}
这样的映射。 Clojure 自动生成那些 getter 和 setter 方法。它们可用于读取和修改基础 class 字段。 gen-class
的 docstring 中记录了这一点。
此方法适用于 public 和受保护的字段。它不适用于私有字段。
假设 Java 基础 class 是这样的:
package fields;
class Base {
public String baseField = "base";
}
生成子 class 的 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
假设有一个 Java class 没有为其所有字段提供 getter 和 setter,我必须用 :gen-class
和 fiddle 扩展它和他们在一起。
如何访问超级class 字段?
我现在想到的最快(也许是最干净...)的解决方案是创建一个 java class 来扩展我的超级 class,然后扩展它相反,但我想知道是否有其他听起来更直接的选择。
谢谢!
我在理解所有细节时遇到了一些困难,因此决定尝试一个最小版本。这是一个文件清单:
> 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 class:
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
或 "package protected"(无限定符)则失败。这是有道理的,因为我们的 subclass 在不同的包中。
此外,如果我给 subclass 一个不同于默认值的名称,即 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中,subclass通常看不到superclass的private成员变量; "package protected" 来自不同包的成员也受到限制。这是讨厌的 Java class:
package jpak;
public class Base {
private long answer = 41;
}
然而,Java 具有超越 private
访问限制的众所周知的能力,您甚至不需要子class!您需要做的就是使用反射。这是 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
和 Method
,它们是反射期间返回的 classes,都包括在内。
生成的 classes 中的方法可以在 gen-class
的 :exposes
选项的帮助下访问基础 class 字段。 :exposes
需要一个映射,其中键是与基础 class 字段名称匹配的符号;值也是像 {:get getterName, :set setterName}
这样的映射。 Clojure 自动生成那些 getter 和 setter 方法。它们可用于读取和修改基础 class 字段。 gen-class
的 docstring 中记录了这一点。
此方法适用于 public 和受保护的字段。它不适用于私有字段。
假设 Java 基础 class 是这样的:
package fields;
class Base {
public String baseField = "base";
}
生成子 class 的 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