更新
这个问题在很大程度上是重复的,但下面的问题和我的答案似乎都更具说明性。有关上一个问题,请参阅Access Java fields dynamically in Clojure?。
我正在尝试从Clojure中查找Java对象实例中的字段设置。点运算符似乎在我天真地期望它工作的地方失败。
例如,有了这些定义......
(defn example-point []
(let [instance (java.awt.Point. 1 2)]
(list (. instance x) (. instance y))))
(defn example-point-1 []
(let [instance (java.awt.Point. 1 2)
fields '("x" "y")]
(map #(. instance (symbol %))
fields)))
(defn example-point-2 []
(let [instance (java.awt.Point. 1 2)
fields (map symbol '("x" "y"))]
(map (fn [field] (eval `(. ~instance ~field)))
fields)))
我得到这些返回值:
flood.core> (example-point)
(1 2)
那很棒,但是如果我想提供字段的名称"以编程方式&#34 ;?这是其他功能应该做的事情。对于我天真的思维方式,他们应该返回与上面相同的价值。但他们都有不同的错误。
flood.core> (example-point-1)
IllegalArgumentException找不到匹配的方法:类java.awt.Point的符号clojure.lang.Reflector.invokeMatchingMethod(Reflector.java:53)
flood.core> (example-point-2)
CompilerException java.lang.RuntimeException:无法在代码中嵌入对象,也可能没有定义print-dup:java.awt.Point [x = 1,y = 2],编译:(/ tmp / form- init1213427540543573506.clj:1:5659)
我被困了,你能帮我解决这个问题吗?
进一步更新
我确实尝试了这个,它又产生了另一个错误。
;; added in response to suggestion...
(defn example-point-3 []
(let [instance (java.awt.Point. 1 2)
fields (map symbol '("x" "y"))]
(map (fn [field] (eval `(. instance ~field)))
fields)))
flood.core> (example-point-3)
CompilerException java.lang.RuntimeException:没有这样的var:flood.core / instance,编译:(/ tmp / form-init1213427540543573506.clj:1:5659)
答案 0 :(得分:3)
以下代码示例以自包含的方式再现了Joost Diepenmaat在Access Java fields dynamically in Clojure?的回答中的主要观点。实际使用时,他的java-fields
包也在Clojars以及on Github,并提供了一个fields
函数,可以应用于Java类的实例。
(import java.lang.reflect.Field)
(import java.lang.reflect.Modifier)
(defn example-point* []
(let [fields (filter #(let [m (.getModifiers %)]
(and (not (= 0 (bit-and Modifier/PUBLIC m)))
(= 0 (bit-and Modifier/STATIC m))))
(.getDeclaredFields java.awt.Point))
instance (java.awt.Point. 1 2)]
(map #(.get % instance) fields)))
即使对于包含一个静态私有字段的Points,也需要进行此过滤,如评估(map #(identity %) (.getDeclaredFields java.awt.Point))
所示。
但是,如果我们从公共字段的已知名称开始,那么以下较短的答案可行,并且稍微接近原始问题的范围。这需要java.lang.reflect.Field
。
(defn example-point** []
(let [field-names '("x" "y")
instance (java.awt.Point. 1 2)]
(map #(.get (.getField java.awt.Point %) instance) field-names)))
请注意,此处根本不使用点运算符。
答案 1 :(得分:2)
第一个错误是clojure评估following form:
的结果(. instance-expr (method-symbol args*))
如果您将列表作为.
特殊表单的第二个参数传递,则将其解释为对参数method-symbol
的方法args
的调用(即(method-symbol args*)
为未评估)。
在第二种情况下,你没有引用instance
不必要的。由于clojure会评估instance
符号并在语法引用的表单中替换它的值,因此您的案例中的结果如下:
(. <instance of point> x)
然后传递给eval。 Eval期望clojure代码进行评估,如果实例不正确(.
必须有一个符号作为第一个参数)。
一种解决方案是在中取消引用instance
example-point-2