Creating Java Objects in Clojure

When calling Java libraries or the Java standard library, you’ll often need to pass in Java objects that either implement an interface or extend a particular class. Clojure provides solutions for this problem in several ways—direct use of Java types and interfaces, anonymous interface implementation with reify, and class extension with proxy.

Direct Use of Java Types

Clojure’s implementation reuses Java’s own concrete types like String, Character, Boolean, the numeric classes, Date, and more. Because these Clojure objects are actually Java objects, they can be passed directly to Java APIs without wrapping or modification.

Additionally, many Clojure objects implement key Java interfaces where applicable, so they play directly with existing APIs without additional effort. For example, Clojure functions implement the interfaces Runnable and Callable making them useful in many concurrency and task-oriented APIs. For example, a Thread is started with a Runnable so any Clojure function can be used:

 (​defn​ say-hi []
  (println ​"Hello from thread"​ (.getName (Thread/currentThread))))
 
 (dotimes [_ 3]
  (.start (Thread. say-hi)))

Each of the last three lines will start a new thread, running the say-hi function, so you should see (in possibly a different order):

 Hello from thread Thread-1
 Hello from thread Thread-2
 Hello from thread Thread-3

Additionally, the Clojure data structures implement key interfaces from the Java Collections API, such as Collection, List, Map, and Set. This means that any Java interface that takes a collection can be directly passed the collection created in Clojure without wrapping or copying the data.

For example, consider the Java library class java.util.Collections, which has helpful utility methods for Java collections. One provided method is binarySearch, which takes a sorted list and uses a binary search algorithm to determine whether the list contains an element. If the list also implements the RandomAccess interface (which Clojure vectors do), this can be accomplished in O(log n) element comparisons.

Since the Clojure collections implement the proper interfaces, they can be passed directly to this Java method:

 (java.util.Collections/binarySearch [1 13 42 1000] 42)
 -> 2

The binarySearch method returns the index in the collection where the element can be found.

Between Clojure’s direct use of Java primitives and extensive use of Java interfaces, a lot of Java interop will simply do what you expect when you invoke a method, both in the code as well as in the underlying bytecode. There’s no magic here, just Clojure adhering to good Java implementation principles.

However, you’ll inevitably encounter cases where you need to invoke a method that takes an instance conforming to a Java interface where you need to provide that instance. In the next section, we’ll see some options for how to do this.

Implementing Java Interfaces

When invoking a Java method takes an instance implementing a Java interface, one of the key questions is whether you need to generate that instance just for the purposes of the call or you need to make and reuse that instance in many places.

In the first case (common when creating callbacks or using event-based APIs), it’s easiest to just create an anonymous instance at the point of use with reify.

For example, the java.io.File class contains a method list(FilenameFilter filter) to obtain a list of files in a directory that satisfy the filter. The FilenameFilter class has just one method, accept(File dir, String name).

Consider building a function list-files that takes a directory and a suffix and returns a sequence of all files with that suffix in the directory:

 (import [java.io File FilenameFilter])
 
 (​defn​ suffix-filter [suffix]
  (reify FilenameFilter
  (accept [this dir name]
  (.endsWith name suffix))))
 
 (​defn​ list-files [dir suffix]
  (seq (.list (File. dir) (suffix-filter suffix))))
 
 (list-files ​"."​ ​".clj"​)
 -> (​"project.clj"​)

When we invoke the list method, we need to pass a suffix-accepting instance of FilenameFilter. Because we have a short-term need for that instance, it’s best to just use reify to create an anonymous instance implementing that interface.

The suffix-filter helper function takes care of creating the instance. The reify function takes an interface, which is followed by implementations of each method in that interface. The first argument to each method is always the anonymous instance itself, which is commonly called this (although there’s nothing special about this name). Any number of interfaces can be implemented in a single call to reify. Any interface methods that are not specified will be added automatically and will throw an UnsupportedOperationException.

In other cases, you’ll want to create an object with data fields and a type that implements a Java interface. Both defrecord and deftype can be used to implement interfaces inline.

For example, consider creating a Counter instance with a field n for how high to count. You could create this with reify, but you’d be unable to get or modify the data field inside the instance, and you’d have no concrete type for the instances.

Instead we could use defrecord to implement Runnable directly:

 (defrecord Counter [n]
  Runnable
  (run [this] (println (range n))))
 -> user.Counter
 
 (​def​ c (->Counter 5))
 -> #​'user/c
 
 (.start (Thread. c))
 -> (0 1 2 3 4)

The advantage here is that c is not just an opaque anonymous object. It has a concrete type (user.Counter), which can be used for polymorphism and a field n that can be retrieved and/or updated.

 (:n c)
 -> 5
 
 (​def​ c2 (assoc c :n 8))
 -> #​'user/c2
 
 (.start (Thread. c2))
 -> (0 1 2 3 4 5 6 7)

To create stateful objects, we extend this further by using fields that hold reference types like atoms.

So far we’ve only looked at implementing interfaces. Next we’ll consider how to deal with the need to extend an actual class.

Extending Classes with Proxies

In addition to interfaces, many Java APIs provide base classes that can be used to ease the implementation of custom instances of an interface. In particular, this is common in places like Swing or XML parsers. Clojure can easily generate one-off proxies or classes on disk when needed.

A good example is parsing XML with a Simple API for XML (SAX) parser. To get ready for this example, go ahead and import the following classes. We’ll need them all before we’re done:

 (import '[org.xml.sax InputSource]
  '[org.xml.sax.helpers DefaultHandler]
  '[java.io StringReader]
  '[javax.xml.parsers SAXParserFactory])

To use a SAX parser, you need to implement a callback mechanism. The easiest way is often to extend the DefaultHandler class. In Clojure, you can extend a class with the proxy function:

 (proxy class-and-interfaces super-cons-args & fns)

As a simple example, use proxy to create a DefaultHandler that prints the details of all calls to startElement:

 (​def​ print-element-handler
  (proxy [DefaultHandler] []
  (startElement [uri local qname atts]
  (println (format ​"Saw element: %s"​ qname)))))

proxy generates an instance of a proxy class. The first argument to proxy is [DefaultHandler], a vector of the superclass and superinterfaces. The second argument, [], is a vector of arguments to the base class constructor. In this case, no arguments are needed.

After the proxy setup, comes the implementation code for zero or more proxy methods. The proxy shown earlier has one method. Its name is startElement, and it takes four arguments and prints the name of the qname argument.

Now you need a parser to pass the handler to. This requires plowing through a pile of Java factory methods and constructors. For a simple exploration at the REPL, you can create a function that parses XML in a string:

 (​defn​ demo-sax-parse [source handler]
  (.. SAXParserFactory newInstance newSAXParser
  (parse (InputSource. (StringReader. source)) handler)))

Now the parse is easy:

 (demo-sax-parse ​"​<foo>
  <bar>Body of bar</bar>
  </foo>​"​ print-element-handler)
 |​ Saw element​:​ foo
 |​ Saw element​:​ bar

The previous example demonstrates the mechanics of creating a Clojure proxy to deal with Java’s XML interfaces. You can take a similar approach to implementing your own custom Java interfaces. But if all you’re doing is XML processing, the clojure.data.xml library already has terrific XML support and can work with any SAX-compatible Java parser.

For one-off tasks, such as XML and thread callbacks, Clojure’s proxies are quick and easy to use. If you need a longer-lived class, you can generate an entirely new class from Clojure using gen-class, which is an advanced topic that won’t be discussed here.

Now that we’ve seen how to invoke Java APIs from Clojure, let’s turn things around and consider how to invoke Clojure from Java.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.28.108