Kurser i Domain-Driven Design - VĂ¥ren 2012




Sunday, October 21, 2012

Reflection in Clojure

I recently got a question about dynamically loading and instantiating Java classes in Clojure. The goal was to check if a certain library was available on the classpath. If it was available it should be loaded and used, if not, a simpler default version would be used instead.

Given Clojure's dynamic nature, it would be reasonable to believe that this is a simple thing. Perhaps it is but, I didn't find an obvious solution to this problem in pure Clojure. Luckily, since Clojure runs on the JVM and interops nicely with Java, there is a pretty straight-forward way of solving this; finding and instantiating the classes needed aren't too hard to accomplish using Java and some good old reflection.

In the code example below the class we are looking for has a useful default constructor. If that's not the case, instantiating the class requires a little bit more work. But once you have a reference to that instance, using it from Clojure is just like using any other Java class from Clojure!

In the example below we prefer to call on Joda Time to provide us with the current date and time, but if the library is not in our classpath, we fall back to using java.util.Date.

(defn exists? [c]
  (let [loader (.getContextClassLoader (Thread/currentThread))]
    (try
      (Class/forName c false loader)
      true
      (catch ClassNotFoundException cnfe false))))

(defn print-java-date-now []
  (println (java.util.Date.)))

(defn print-joda-date-now []
  (let [clazz (Class/forName "org.joda.time.DateTime")]
    (println (.newInstance clazz))))

(if (exists? "org.joda.time.DateTime")
  (print-joda-date-now)
  (print-java-date-now))

Update 2012-10-22:

My colleague Mattias came up with this much nicer version of the exists? function using clojure.reflect/resolve-class:

(defn exists? [c] 
  (resolve-class (.getContextClassLoader (Thread/currentThread)) c))

4 comments:

Unknown said...

Thanks for this a really nice little reference about dynamic class loading. After seeing the second version of exists? I looked at the Clojure docs said about resolve-class and it's really sparse you should add this example!

Patrik said...

Thanks for your comment! I have added the example to ClojureDocs

gtrak said...

There is a variation of this technique in clojure.tools.logging that makes use of eval to create static (dynamic) code. Check out https://github.com/clojure/tools.logging/blob/master/src/main/clojure/clojure/tools/logging/impl.clj#L35 . This can provide greater performance than reflection in the active code path. Eval, protocols and reify would make a powerful combo, I'm just happy it all exists already!

Patrik said...

Great reference, thanks!