Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

clojure - how (and when) to invoke the dot operator?

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)

like image 293
Joe Corneli Avatar asked Jun 02 '15 13:06

Joe Corneli


People also ask

What is the purpose of the dot operator?

(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.)

What is dot operator with example?

In simple words, we can say that the dot operator is actually access provider of objects and classes. For example: objectReference. methodName(argumentList);

Why do we use dot operator in Java?

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.


1 Answers

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.

like image 72
Joe Corneli Avatar answered Sep 20 '22 12:09

Joe Corneli