5
votes

When working with existing java classes I often get reflection warnings if I've done something incorrectly, e.g.

IllegalArgumentException No matching field found: gets for class java.lang.String clojure.lang.Reflector.getInstanceField (Reflector.java:271)

Is clojure doing reflection at runtime for each invocation of the given methods? or is this cached in any sort of way? Would there be a speed benefit to moving any kind of involved java-interop into a related java class?

2

2 Answers

6
votes

Clojure will do reflection at runtime only if it can't infer the exact method to call based on the surrounding context, otherwise it emits code that will call the method directly. You can use type hints to provide the compiler with this context if needed. For instance:

user=> (set! *warn-on-reflection* true)

user=> (defn str-len [x] (.length x))
Reflection warning, NO_SOURCE_PATH:1:19 - reference to field length can't be resolved.

user=> (defn str-len-2 [^String x] (.length x))

user=> (str-len "abc") ; -> 3
user=> (str-len-2 "abc") ; -> 3

user=> (time (dotimes [_ 100000] (str-len "abc")))
"Elapsed time: 1581.163611 msecs"
user=> (time (dotimes [_ 100000] (str-len-2 "abc")))
"Elapsed time: 36.838201 msecs"

The first function will use reflection every time it's invoked; the second has similar performance to native Java code.

4
votes

That isn't a reflection warning, it is just an indication that it was using reflection.

You can use type hints to eliminate reflection. The *warn-on-reflection* flag as described in the above link (default false), optionally enables reflection warnings.

I find it convenient to use Leiningen's lein check utility, which attempts to compile every Clojure file in your project, with reflection warnings turned on. This will report reflection issues in your code, or in any code loaded from libraries.