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?
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).
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"))
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