Clojure's Java Interoperability
As part of my residency here at 8th Light, I was tasked with connecting my Clojure tic-tac-toe library to my Java HTTP server. I had to learn how to use Clojure’s Java interoperability features before I could work on this project–here’s a bit of what I learned:
Instantiating Classes
Initializing a new instance of a Java class is dead simple. Consider initializing a new String, for example, in Java:
Let’s try the same exact thing in Clojure:
See? That wasn’t so hard. The key thing to notice is the period after java.lang.String
. This period tells the compiler to create a new instance of the class of whatever was before it; in this case, java.lang.String
. You could do the same thing with Java’s Integer class:
Calling Methods
Let’s say we want to use some of Java’s String functions. This, too, is quite simple:
Notice the period before we instantiate my name as a String; this tells Clojure to treat this statement as Java code and run a Java method named length
on the String instance provided.
We can even call multiple methods on an instance of a Java class by using Clojure’s doto
function:
Accessing Fields
The syntax for getting a Java field is similar to calling methods. Let’s imagine we have a very simple Sushi class with a field containing the type of fish in the roll:
Now let’s access the fishType
field from Clojure:
The first period (within the parentheses) again tells us to evaluate this bit of code as Java, then we provide an instance of the Sushi class and finally provide the field name we’re looking for.
Creating New Classes
Creating new Java classes in Clojure is, however, a bit more involved. To start, we’ll need to create a new namespace for our class and then give it a class name and a method prefix by using the :gen-class
namespace clause:
:name
simply sets a name for the class that we’ll use when we want to construct a new object, in this case, we’ve set it to examples.Sushi:
:prefix
is a string or symbol essentially used to namespace methods. Something to note is that if you don’t provide a :prefix
in the :gen-class
namespace clause, the default prefix is -. This tripped me up more than a few times!
Since we used “method-“ as our prefix, all of our methods must be prefixed with “method-“ in order to use them:
Extending Classes
Imagine you’ve written a pretty sweet Java class that you’d like to extend in Clojure. First, you must import
the Java class you’d like to extend, then add an :extend
statement to your namespace’s :gen-class
clause:
So what if we wanted to override a method defined in our SpicyTunaMaki
Java class? Use :exposes-methods
:
Notice we used the method-
prefix on each method name and that we specified each method’s superclass name and override name in the :exposes-methods
statement.
Conclusion
Overall, I was pleased with the straightforwardness of most of this interop stuff. I used Leiningen for my project (I highly recommend it!) and ran into an issue where my Clojure module containing my Java interop code wasn’t compiling before my Speclj tests ran. All I had to do was run lein compile
and everything worked fine.
I hope this brief overview helps you in your Clojure interop adventures!