Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure - Reflection Warning with primitive array .length

I'm having some trouble trying to use instance methods on a java float array in clojure. Specifically, the following code gives a reflection warning on the call to .length

(defn make-matrix
  ([^floats fs]
   (let [len-fs (.length fs)]
     (cond (>= len-fs 16) (Matrix4. fs)
           (>= len-fs 9)  (Matrix3. fs)
           :else (throw (IllegalArgumentException. (str "Array must have at least 9 elements (found" len-fs ")")))))))

By my understanding, the type-hint should eliminate the need for reflection to resolve the call to .length. What am I missing?

like image 644
John Sullivan Avatar asked Dec 12 '13 05:12

John Sullivan


2 Answers

Arrays on the JVM are not quite like regular objects and their lengths are not obtained through a field lookup, but rather through a specialized opcode arraylength. Java's array.length notation is just syntactic sugar that compiles down to this opcode. Therefore, (.length fs) will simply fail at runtime, because fs will be found to have neither a length method nor a length field (assuming it is in a fact an array).

Thus, you should use the clojure.core/alength function:

(alength fs)

Without a type hint, this will result in a reflective call; with a type hint, it will be a direct call to a static method returning fs.length (in Java notation).

like image 78
Michał Marczyk Avatar answered Sep 18 '22 17:09

Michał Marczyk


Primitive arrays are a somewhat special case in the JVM, a proof of this is that there's a bytecode instruction for getting the length of them: arraylength. So the reason why Clojure is showing a reflection warning is because the class for arrays of floats [F does not declare a field with the name length, so when it tries to get it (here) the field is not there.

You can check it yourself doing the following:

(-> (float-array [1])
  class
  (.getField "length"))
like image 45
juan.facorro Avatar answered Sep 19 '22 17:09

juan.facorro