Clojure's Java Interoperability

March 28, 2013

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:

String myName = new String("Rylan");

Let’s try the same exact thing in Clojure:

(java.lang.String. "Rylan")
  => "Rylan"

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:

(java.lang.Integer. 1337)
  => 1337

Calling Methods

Let’s say we want to use some of Java’s String functions. This, too, is quite simple:

(.(java.lang.String. "Rylan") length)
  => 5

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:

(doto (java.util.ArrayList.)
  (.add "Candy")
  (.add "Cookies")
  (.add "Ice Cream"))
  => ["Candy" "Cookies" "Ice Cream"]

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:

class Sushi {
  public String fishType;

  public Sushi(String fishType) {
    this.fishType = fishType;
  }
}

Now let’s access the fishType field from Clojure:

(. (Sushi. "Tuna") fishType)
  => "Tuna"

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:

(ns examples.sushi
  (:gen-class
   :name examples.Sushi
   :prefix method-))

: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:

(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:

(ns examples.sushi
  (:gen-class
    :name examples.Sushi
    :prefix method-))

(defn method-explain-this-roll [this]
  "This is a generic roll of sushi.")

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:

(ns examples.sushi
  (import org.myapp.SpicyTunaMaki)
  (:gen-class
    :name examples.SuperSpicyTunaMaki
    :prefix method-
    :extends org.myapp.SpicyTunaMaki))

(defn method-explain-this-roll [this]
  "This is the spiciest tuna maki IN THE WORLD.")

So what if we wanted to override a method defined in our SpicyTunaMaki Java class? Use :exposes-methods:

(ns examples.sushi
  (import org.myapp.SpicyTunaMaki)
  (:gen-class
    :name examples.SuperSpicyTunaMaki
    :prefix method-
    :extends org.myapp.SpicyTunaMaki
    :exposes-methods {roll exposed-roll
                      serve exposed-serve}))

(defn method-explain-this-roll [this]
  "This is the spiciest tuna maki IN THE WORLD.")

(defn method-exposed-roll [this]
  "Rolling some spicy goodness")

(defn method-exposed-serve [this]
  "You're going to need some water for this one")

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!