Update
This question is largely a duplicate, but both the question and my answer below seem more expository. See Access Java fields dynamically in Clojure? for the previous question.
I am trying to look up the settings of fields in instances of Java objects from Clojure. The dot operator seems to fail in places where I would naively expect it to work.
For example, with these definitions in place...
(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)))
I get these return values:
flood.core> (example-point)
(1 2)
That's great, but what if I want to supply the name of the field "programmatically"? That's what the other functions are supposed to do. To my naive way of thinking, they should return the same value as above. But they both give different errors.
flood.core> (example-point-1)
IllegalArgumentException No matching method found: symbol for class java.awt.Point clojure.lang.Reflector.invokeMatchingMethod (Reflector.java:53)
flood.core> (example-point-2)
CompilerException java.lang.RuntimeException: Can't embed object in code, maybe print-dup not defined: java.awt.Point[x=1,y=2], compiling:(/tmp/form-init1213427540543573506.clj:1:5659)
I'm stuck, can you help me figure this out?
Further update
I did try this, and it produces yet another error.
;; 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: No such var: flood.core/instance, compiling:(/tmp/form-init1213427540543573506.clj:1:5659)
(dot) operator is used to access class, structure, or union members. The member is specified by a postfix expression, followed by a . (dot) operator, followed by a possibly qualified identifier or a pseudo-destructor name. (A pseudo-destructor is a destructor of a nonclass type.)
In simple words, we can say that the dot operator is actually access provider of objects and classes. For example: objectReference. methodName(argumentList);
Simply the dot operator acts as an access provider for objects and classes. The usage of the above operator is as below. It separates a function and variable from an instance variable. It allows accessing sub-packages and classes from a package.
The following code sample reproduces the central ideas from Joost Diepenmaat's answer at Access Java fields dynamically in Clojure? in a self-contained way. For practical use, his java-fields
package is on Clojars as well on Github, and supplies a fields
function that can be applied to instances of Java classes.
(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)))
This filtering is necessary for even for Points, which contain one static private field, as can be seen by evaluating (map #(identity %) (.getDeclaredFields java.awt.Point))
.
However, if we start with known names of the public fields, then the following shorter answer works, and is somewhat closer to the spirt of the original question. Again this requires 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)))
Notice that the dot operator is not used here at all.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With