Search code examples
clojureclojure-java-interop

Clojure optimization of java interop


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?


Solution

  • 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.