Chapter 10. Java Interoperability

Calling Java from Clojure

Clojure is built on Java not only because it is a portable, feature-rich platform, but because thousands of libraries, both open-source and commercial, are written in Java. Clojure can leverage all this existing code to get past the "library problem" that plagues most new programming languages.

Clojure does not come packaged with libraries to handle common tasks like file I/O, networking, and database connections. While the number of extant Clojure libraries is growing rapidly, it is still quite small. Fortunately, for any task you might have in mind, there almost certainly exists a Java library to help you with it. The JVM itself comes with over 4000 classes covering everything from networking to GUIs. Clojure is designed to make working with Java libraries as seamless as possible.

Java Interop Special Forms

Clojure uses just three special forms to handle all interactions with Java classes. The new special form creates an instance of a class.

(new classname & constructor-arguments)

new takes the name of a class (a symbol, which will not be evaluated) as its first argument, followed by any arguments for the class's constructor function. The following are some examples:

user> (new String)
""
user> (new java.util.Date)
#<Date Thu Oct 29 17:04:19 EDT 2009>
user> (new java.util.Date 55 10 12)
#<Date Sat Nov 12 00:00:00 EST 1955>

The . (dot) special form calls Java methods or fields.

(. target name & arguments)

The target argument may be either a class name or an arbitrary expression. If the target argument is a class name, then the name should be a symbol (which is not evaluated) naming a public static method or field of that class. For example, the following code:

user=> (. Integer valueOf "42")
42
user=> (. Integer MAX_VALUE)
2147483647

These are equivalent to this Java code:

Integer.valueOf(42);
Integer.MAX_VALUE;

If target is not a class name, then it will be evaluated normally, and name should be the name of a public instance method or field of the resulting object. Here are some examples:

user=> (def s "Hello, World!")
#'user/s
user=> (. s substring 0 5)
"Hello"

The second expression is equivalent to the Java code:

s.substring(0, 5);

To set the value of public fields, you can use the set! special form like this:

(set! (. target name) value)

As shown, target is an object or symbol naming a class, name is a symbol naming a public field of that class or object, and value is any expression. This is equivalent to the Java code:

target.name = value;

The new, . (dot), and set! special forms are just that, special. They do not obey the same rules for evaluation as normal Clojure functions and macros. In particular, the name argument is never evaluated, so it cannot be determined at run-time. You cannot, for example, do the following:

;; bad code!
(defn call-method [object method-name]
  (. object method-name))

That will try to call a method named "method-name" on the object—probably not what you wanted. If you need to determine the name of a method at run-time, there are two ways to achieve it: the Java Reflection API and Clojure's eval function. The former is preferred, but consult the Reflection API documentation for details.[6]

Java Interop Preferred Forms

While the new and . (dot) special forms are sufficient for Java interop, some additional syntax helps Java fit better with Clojure's Lisp-based syntax.

First, Java method calls can be made to look more like Clojure function calls by putting the method name at the head of a list, prefixed by a period:

(.method object arguments)

The .method form will be processed by the Clojure compiler as if it were a macro that expands to:

(. object method arguments)

By "as if it were a macro," I mean that this feature is a purely syntactic abstraction or "syntactic sugar." It does not magically transform Java methods into first-class functions.[7] For example, you cannot use .method as a function argument to map. Instead, you must wrap the method in a Clojure function:

user=> (map #(.toUpperCase %) ["one" "two" "three"])
("ONE" "TWO" "THREE")

The Clojure macro memfn was created for this purpose before the anonymous function syntax #() existed. memfn takes a symbol and expands to an anonymous function that calls the method named by that symbol. The anonymous function in the preceding example could have been written (memfn toUpperCase), but the #() form is shorter and preferred.

New instances of Java classes can be constructed by placing the class name at the head of a list, followed by a period:

user=> (java.util.Date. 110 3 12)
#<Date Mon Apr 12 00:00:00 EDT 2010>
user=> (StringBuilder. "Hello")
#<StringBuilder Hello>

You can call static methods with the syntax (ClassName/method arguments) and retrieve the value of a static field with ClassName/field. For example, the following code:

user=> (Integer/parseInt "101")
101
user=> Integer/MIN_VALUE
-2147483648

Since these "syntactic sugar" expansions happen in the same compilation phase as macro-expansion, macros that do complex code-generation may need to avoid them and use the new and . (dot) special forms directly. In all other cases, the "syntactic sugar" forms are preferred.

Clojure Types and Java Interfaces

One of Java's strengths as a platform is the provision of generic interfaces for common datatypes such as lists and sets. Clojure's data structures implement these interfaces where appropriate, so if you need to call a Java method that expects, for example, a java.util.List, you can pass it a Clojure data structure without any conversion. Table 10-1 shows which interfaces are implemented by each of the built-in Clojure types.

Table 10-1. Standard Java Interfaces Implemented by Clojure Types

Java interface

list

vector

map

set

function

java.util.Collection

Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types

--

Standard Java Interfaces Implemented by Clojure Types

--

java.util.List

Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types

--

--

--

java.util.Map

--

--

Standard Java Interfaces Implemented by Clojure Types

--

--

java.util.Set

--

--

--

Standard Java Interfaces Implemented by Clojure Types

--

java.util.RandomAccess

--

Standard Java Interfaces Implemented by Clojure Types

--

--

--

java.lang.Iterable

Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types

--

java.lang.Comparable

--

Standard Java Interfaces Implemented by Clojure Types

--

--

--

java.lang.Runnable

--

Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types

java.util.concurrent.Callable

--

Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types
Standard Java Interfaces Implemented by Clojure Types

java.util.Comparator

--

--

--

--

Standard Java Interfaces Implemented by Clojure Types

Be aware that Clojure's collection types (list, vector, map, and set) are still immutable, so they only implement the read-only portions of the java.util.Collection interfaces. Calling a mutating method (such as List.add or Map.put) on an immutable object will throw an UnsupportedOperationException.

What about Java generics like List<String> or Map<Integer,Object>? Fortunately, Clojure code never needs to worry about generics due to the way they are implemented in the JVM. Generic types are ignored in Java bytecode; they exist only as hints to the Java language compiler.[8] The Java type List<String>, when compiled, is just plain List. What this means for Clojure is that you can call a Java method expecting a generic type (e.g., List<String>) with an instance of the base collection type (List). As long as the collection contains objects of the correct type (String), it just works.

Java Arrays

Java arrays lack the concurrency safety of Clojure's collection types; they are mutable and non-thread-safe. However, some Java APIs use arrays for function arguments or return values, so it is necessary to work with them.

In addition, some algorithms can be implemented more efficiently with primitive arrays, especially algorithms that deal with very large collections of primitive types. Using primitive arrays for performance will be discussed in Chapter 14.

Creating Arrays

You can create a Java array with the make-array function:

(make-array type & dimensions)

The type argument must be a class. If you want an array of a Java primitive type, such as int or double, you can use the TYPE field of the corresponding class:

(make-array Double/TYPE 40)   ;; creates a double[40] array

If you give only one dimension to make-array, you get a normal Java array of that length. If you give multiple dimensions, you get a multidimensional array, which is implemented in Java as an array of pointers to other arrays.

In addition to make-array, there are convenience functions for creating arrays of Java primitive types: int-array, long-array, float-array, and double-array. Each can be called in several argument forms:

  • (int-array size) creates an int[] array of size elements.

  • (int-array size initial-value) does the same and also sets every element to initial-value.

  • (int-array collection) creates an int[] array of the same size as collection, filled with the elements of collection converted to ints.

  • (int-array size collection) creates an int[] array of size elements and fills it with elements from collection; any unused array elements will be initialized to zero.

The Clojure function to-array takes any Clojure collection type and returns a Java Object[] array. If you have a two-dimensional matrix represented as a collection of collections, you can use the to-array-2d function to produce a 2-dimensional Java array. For example, the following code:

user=> (def matrix [[1 0 0] [0 1 0] [0 0 1]])
#'user/matrix
user=> (to-array-2d matrix)
#<Object[][] [[Ljava.lang.Object;@540984b>

If you need to convert a collection into an array of a specific type, you can use the into-array function:

(into-array collection)
(into-array type collection)

Called with one argument, a collection, into-array returns an array of the same type as the first item in the collection. Called with two arguments, the first argument is a class specifying the type of the array. For example, the following code:

user=> (into-array Comparable ["aa" "bb" "cc"])
#<Comparable[] [Ljava.lang.Comparable;@a00185>

Manipulating Arrays

You can retrieve a single value from an array (of any dimensionality) with the aget function:

(aget array & indices)

Setting elements in an array is complicated by the need for special functions for primitive arrays. The aset function works on any arrays of any Object type:

(aset array index value)
(aset array & indices value)

The setter functions for arrays of primitive types work the same way: aset-boolean, aset-byte, aset-char, aset-short, aset-int, aset-long, aset-float, and aset-double. Note that these functions are not very efficient; in fact, they are slower than aset on type-hinted arrays (see Chapter 14). Use them only when you need to deal with small arrays for Java interop purposes, not for performance.

You can copy an array with the aclone function, and get its length with the alength function (although count also works).

Iterating Over Arrays

The map and reduce functions will work on Java arrays, but they work by converting the arrays to sequences. For slightly greater efficiency, you can iterate over arrays directly using array-specific macros.

(amap a idx ret expr)

The amap macro initializes ret (a symbol) as a clone of the array a, then evaluates expr repeatedly with idx bound to successive indexes of a. Whatever value is returned by expr, it will be assigned to the same index of ret. Finally, amap returns ret.

(areduce a idx ret init expr)

The areduce macro assigns ret (a symbol) the value of init, then evaluates expr repeatedly with idx bound to successive indexes of the array a. Whatever value is returned by expr becomes the new value of ret. Finally, areduce returns the last value of ret.

Note that both amap and areduce are macros implemented in terms of loop/recur, so they take expressions as arguments instead of the functions used by map and reduce.

Calling Clojure from Java

Clojure code can generate real Java classes and methods that can be called like any other Java class. However, if you need to call just a few Clojure functions from your Java code, it may be simpler to user Clojure's Java API, which consists of static methods of the classes clojure.lang.RT, clojure.lang.Compiler, and clojure.lang.Var.

Loading and Evaluating Clojure Code

clojure.lang.RT is the Clojure "runtime" class. Remember, Clojure has no interpreter; there cannot be multiple "instances" of Clojure in a single JVM.[9] As a result, most methods of RT are static.

class RT {
    ...
    public static void load(String name);
    public static void loadResourceScript(String filename);
    public static void maybeLoadResourceScript(String filename);
    ...
}

The RT.load method behaves just like the Clojure load function described in Chapter 7. The name argument is the name of a file on the classpath, minus the ".clj" or ".class" extension.

The RT.loadResourceScript method is similar to load, except that filename must include the ".clj" extension. RT.maybeLoadResourceScript is the same, but will not throw an exception if the file does not exist.

class RT {
    ...
    public static Object readString(String code);
    ...
}
class Compiler {
    ...
    public static Object eval(Object obj);
    ...
}

The RT.readString method is equivalent to the Clojure read-string function; it takes a string of Clojure source code and returns the data structure represented by that string. The Compiler.eval method will evaluate that data structure just like the Clojure eval function and return the result.

Using Clojure Functions and Vars

class RT {
    ...
    public static Var var(String ns, String name);
    public static Var var(String ns, String name, Object value);
    ...
}

The RT.var method returns the Clojure Var with the given namespace and name, creating the namespace and interning the Var (see Chapter 7) as needed. The optional third argument sets the initial value, or root binding, of the Var.

Once you have a Var object, you can retrieve its value with Var.get, or call it as a function with Var.invoke:

class Var {
    ...
    public Object get();
    public Object invoke(Object args...);
    ...
}

Creating Java Classes

Often, Clojure's Java API will not be sufficient for integrating Java code with Clojure code. Many Java libraries require you to implement a particular interface or extend a particular base class. Fortunately, Clojure can create real Java classes, with methods that can be called like any other Java method, without requiring you to write any "wrapper" code in Java.

Proxying Java Classes

If you need to implement a Java interface or extend a base class for Java interop purposes, the proxy macro should be the first place you look. Each time proxy is evaluated, it creates a new instance of a proxy class, an anonymous class that inherits from the base class and/or interfaces you specify.

(proxy [base-class-and-interfaces...] [constructor-args...]
  (methodName [params...]  method-body...)
  (methodName ...))

The first argument to proxy is a vector of class and interface names. There may be at most one class (because Java only allows single-class inheritance) and any number of interfaces. If no base class is specified, the proxy class will extend java.lang.Object.

The second argument is a vector of values that should be passed as arguments to the base-class constructor. If the constructor takes no arguments, the vector will be empty, but it must be supplied.

The remaining arguments to proxy are lists of the form (method [args] body), where method is the name of a public or protected member of one of the base classes, args are the arguments to that method, and body is the Clojure code that you want to use to implement the method. In effect, you're defining a Clojure function that will be called by the proxy class whenever the named method is invoked.

Multiple-arity methods (methods that take different numbers of arguments) may be implemented like multiple-arity Clojure functions:

(method ([arg] body...) ([arg1 arg2] body...))

All of this sounds complicated, but it's really not. Let's look at a real example. The Java SAX classes implement stream-based XML processing with a "push" interface. To use them, you must provide an instance of a class that implements the org.xml.sax.ContentHandler interface. Clojure's own XML libraries use proxy for this. Here's a simpler example, a proxy ContentHandler that prints out all the text nodes in the XML document, one per line.

(import '(javax.xml.parsers SAXParserFactory)
        '(org.xml.sax ContentHandler)
'(org.xml.sax.ext DefaultHandler2)
        '(java.io File))

(defn proxy-handler []
  (proxy [DefaultHandler2]
    []  ;; DefaultHandler2 constructor takes no args
    (characters [ch start length]
       (println (String. ch start length)))))

(defn extract-text [filename]
  (let [parser (.newSAXParser (SAXParserFactory/newInstance))]
    (.parse parser (File. filename) (proxy-handler))))

The proxy-handler function returns an instance of a proxy for the class org.xml.sax.ext.DefaultHandler2, which provides no-op implementations of all the org.xml.sax.ContentHandler methods. The proxy class overrides the characters method, which receives a char array, and prints the String form of that array. The extract-text function creates a new instance of SAXParser using the SAXParserFactory class, then calls the parse method with the input file and the proxy handler. After loading this code, you can run it like this:

user=> (extract-text "path/to/some/file.xml")

This will print all the text in the XML file. There will be a lot of blank lines, because your implementation does not ignore text elements consisting entirely of whitespace.

Proxy methods can access the object on which they were called as the special local variable this. For example, to access the value of a public instance field named foo in the current object, a proxy method could call (.foo this).

It is important to remember that proxies are not true subclasses. Although proxies can override protected methods, they cannot access private or protected fields of their "parent" class. They cannot provide their own constructor functions, and they cannot add new methods that are not defined in a parent class or interface. Proxy instances have generated class names like clojure.proxy.org.xml.sax.ext.DefaultHandler2.

Proxy methods do not have direct access to the superclass object as with Java's super keyword. However, proxies can call superclass methods with the proxy-super macro:

(proxy-super method & args)

method is a symbol (unevaluated) naming a superclass method, args are the arguments to that method. The corresponding method in the proxied superclass will be invoked on the current object (this).

Generating Java Classes

While proxy is usually sufficient for dealing with Java APIs, there are occasions when nothing but a real, concrete Java class will do. You can create such classes in Clojure with the gen-class macro, which takes a series of key-value pairs as arguments:

(gen-class
  :name            generated-name
  :extends         base-class-name
  :implements      [interfaces ...]
  :init            initialization-function
:constructors    {[types ...] [super-types ...], ...}
  :post-init       post-initialization-function
  :methods         [[name [types ...] return-type], ...]
  :main            boolean
  :factory         factory-name
  :state           state-field-name
  :exposes         {field {:get name, :set name}, ...}
  :exposes-methods {method exposed, ...}
  :prefix          string
  :impl-ns         namespace
  :load-impl-ns    boolean)

No way around it, gen-class has a ton of parameters. Fortunately, they're all optional except :name, and you rarely need more than a few of them. Before you get into the options, let's look at how gen-class works with Java.

When you compile a Java source file with javac, you get a Java .class file containing Java bytecode. The bytecode defines the fields and methods of that class and their implementations. When you run java, the Java Virtual Machine loads the .class file and executes the bytecode it contains.[10]

Clojure, by contrast, generates bytecode at run time. You can type an expression at the Clojure REPL, or load a .clj file, and Clojure will compile it on-the-fly into Java bytecode, then pass that bytecode to the Java Virtual Machine for execution. This is fine when all your code is in Clojure, but becomes a problem when you want Java code to be able to call Clojure code, because the executable bytecode for Clojure functions doesn't exist until runtime!

Conceivably, you could write a small "wrapper" class in Java, whose methods invoke Clojure functions through Clojure's API, like this:

import clojure.lang.RT;

class MyWrapper {
    public static Object doStuff() {
        return RT.var("my-namespace", "do-stuff").invoke();
    }
}

Then, your Java code could call the method MyWrapper.doStuff(), which invokes the Clojure function my-namespace/do-stuff.

Essentially, gen-class does the same thing, without you having to write any Java code. It generates a Java .class file containing "stub" methods that call Clojure functions.

Because gen-class needs to generate a .class file, which will presumably be used by other statically-compiled Java classes, it cannot be used at runtime. Instead, it must be invoked in a separate compilation step. Clojure normally compiles code at runtime, so compiling Clojure code before it is run is called ahead-of-time, or AOT, compilation.

Ahead-of-Time Compilation

Any Clojure namespace can be AOT-compiled. There is usually little reason to do so unless gen-class is involved. AOT-compiled Clojure code is not faster than dynamically-compiled code, and it still requires the Clojure runtime libraries (clojure.jar). However, AOT-compiled code will start up slightly faster, because the Clojure compiler does not need to compile all the source code when it is loaded, which may be beneficial for large programs.

To compile a namespace, use the compile function:

(compile name)

The name argument is a quoted symbol naming the namespace you want to compile. Clojure will load the source file for that namespace, using the same rules as require for converting namespace names to file names (see Chapter 7); compile it to Java bytecode and write the bytecode out to .class files in a target directory. One namespace will produce many .class files, one for each function.

The tricky part of AOT-compilation is getting the classpath configured correctly. The target directory where compile writes .class files is stored in the Var *compile-path*. When you call compile, both this directory and the source .clj file must be available on the Java classpath. The default *compile-path* is "classes", assumed to be a directory within the current working directory. You can change it on the Java command line by setting the Java system property "clojure.compile.path".

Here's an example. Suppose you have a project containing three directories: source code in "source", compiled code in "target", and libraries in "lib". Your Clojure code is in the file "source/com/example/my_library.clj", with the following namespace declaration:

(ns com.example.my-library)

To compile this namespace, you can start Clojure from the root directory of your project like this (all on one line):

java -cp lib/clojure.jar:sources:target    
Ahead-of-Time Compilation
-Dclojure.compile.path=target clojure.main

Note that the classpath contains three elements: the Clojure JAR file, the "sources" directory, and the "target" directory. (You would add JAR files for any other libraries your project uses.) In addition, the system property clojure.compile.path is set to "target". The "target" directory must exist! Then, at the Clojure REPL, you can run:

user=> (compile 'com.example.my-library)

This will load the source file from "source/com/example/my_library.clj", compile it, and write a bunch of .class files in the directory "target/com/example/".

Once this is done, you can load and use the namespace com.example.my-library without the original source files. All you need are the .class files and clojure.jar. Obviously, you shouldn't delete your source files, because you might want to change them and recompile later.

To make it easier to integrate AOT-compilation into build scripts, you can start Java with the class clojure.lang.Compile instead of clojure.main, setting up the classpath and system property as before, passing the namespaces to be compiled as arguments on the command line. In the Apache Ant build system, for example, the XML configuration would contain something like the following snippet:

<java classname="clojure.lang.Compile"
      classpath="clojure.jar:target:source">
  <sysproperty key="clojure.compile.path"
               value="target"/>
  <arg value="my.first.namespace"/>
  <arg value="my.second.namespace"/>
</java>

How does gen-class fit into this? When compile is compiling a file that calls gen-class, it generates the additional .class files described by the gen-class configuration options. At any other time, i.e., when not AOT compiling, gen-class does nothing.

Basic gen-class Options

Now you're ready to tackle the options to gen-class. You will usually only need the first three options, :name, :extends, and :implements, but we cover them all here. Wherever the arguments call for a class or interface name, that name may be given as either a symbol (which will not be evaluated) or a String, and must be fully-qualified with the Java package name.

The :name argument is the name of the class to be generated. Remember that this is a Java-style package + class name, so you must use underscores or CamelCase instead of hyphens.

The :extends argument is the fully-qualified name of a Java class (not an interface) as either a String or a symbol. The generated class will be a subclass of that class.

The :implements argument is a vector of Java interface names. The generated class will be declared to implement those interfaces and will include stub methods for all the methods defined in those interfaces.

Defining Methods for the Generated Class

As explained earlier, the class generated by gen-class will only contain stub methods. The implementations of those methods are normal Clojure functions in a namespace. Each Clojure function will have the same name as its corresponding method, with an added prefix. The prefix defaults to "-", and can be changed with the :prefix argument to gen-class. The functions will be called with the object instance as their first argument. For example, if your generated class implements a Java interface with the methods doStuff(int i) and doMoreStuff(String s), your namespace should contain the following function definitions:

(defn -doStuff [this i] ...)
(defn -doMoreStuff [this s] ...)

By default, gen-class uses the current namespace to look up method definitions; this can be changed with the :impl-ns argument to gen-class.

Adding State to the Generated Class

You may want to create a class that can be called by Java code but preserves Clojure's notions of immutable state. The :state argument names a public instance field (of type Object) that will be added to the generated class. Within your methods, you can access the value of this field just like any other Java field. Note that the :state field is declared final, so it may not be set outside of the object constructor. Typically, the value of the :state field will be one of Clojure's mutable reference types (Ref, Agent, or Atom). In this way, you can create stateful Java objects that take advantage of Clojure's transactional semantics.

If your object has :state, you must provide a way to initialize it. The :init argument names an "initialization function" that is called before the superclass constructor, with the same arguments as the constructor. The initialization function must return a vector like [[args...] state], where state is the value of the :state field and args are the arguments that will be passed back to the superclass constructor.

To do additional computation after the superclass constructor, the :post-init argument names a function that will be called immediately after the superclass constructor(s), with the newly-constructed object as its argument. The :post-init function's return value is ignored.

Adding Methods to the Generated Class

By default, the generated class contains stub methods for all non-private methods of the parent class and interfaces. If you want to add to this set of methods, you can do so with the :methods option to gen-class. Its argument is a vector of method signatures, each of the form [name [arg-types...] return-type]. Those methods are then implemented by Clojure functions with prefixed names just like superclass methods. To create a static method, add :static true metadata to the signature vector.

For example, suppose you want to add two methods to your class with the following Java signatures:

public int add(int a, int b);
public static String getNextID();

You would use gen-class like this:

(gen-class ...
  :methods [[add [int int] int]
            #^{:static true} [getNextID [] String]])
...
(defn -add [this a b] ...)
(defn -getNextID [] ...)

Remember that :methods is only used for adding methods that do not exist in the superclass/superinterfaces. You do not need to redefine the signatures of existing Java methods.

Adding Constructors and Factories

The generated class will automatically have public constructors with type signatures matching those of the superclass constructors. You can add additional constructors with the :constructors option to gen-class. The argument to :constructors is a map of the form {[types...] [super-types...], ...}. The keys of the map are vectors of argument types for the added constructors, which must map to an existing superclass constructor, identified by a vector of its argument types. For example, if your generated class :extends a class Foo with a constructor Foo(int, int), and you want to add a constructor that takes a single String argument, you can do so with the following gen-class form:

(gen-class ...
  :constructors {[String] [int int]} ...)

You must also supply an :init function that accepts and returns the appropriate types.

Some Java development styles encourage static factory methods instead of public constructors. You can add static factory methods to your generated class with the :factory option to gen-class. Its argument is the name of the generated factory method; this method will be overloaded to accept all the same argument types as the constructors.

Exposing Superclass Members

Because your method implementations are Clojure functions, not true Java methods, they do not have access to protected fields of the superclass, nor can they call superclass methods. To work around this, you can add the :exposes and :exposes-methods options to gen-class.

:exposes takes a map of the form {field {:get getter, :set setter}, ...}. Each key is the name of a protected instance field of the superclass, the value specifies the names of public getter and setter methods that will be added to the generated class. You do not need to provide implementations for these methods; they are generated automatically.

:exposes-methods takes a map of the form {super exposed, ...}, where super is the name of a superclass method, and exposed is the name of a public method that will be added to the generated class. The exposed method calls the super method. You can use this feature when, for example, your implementation of a method needs to call the superclass version of the same method.

Generating Command-Line Programs

Java allows any class to be run as command-line executable, provided it has a method declared public static void main(String[] args). You can specify :main true in gen-class to add the static main method to your generated class. The function implementing this method should be called -main (unless you changed the prefix). Rather than a single array argument, it will be called with however many arguments are present on the command line. An easy way to handle this is to define the function to take a variable number of arguments:

(gen-class ...
  :main true ...)
(defn -main [& args] ...)

Once you have compiled a namespace with a :main method, you can execute it at the command line like this:

java -cp ...  your.class.name  arguments...

Remember that your compiled .class files and the Clojure JAR must be on the classpath.

Loading the Implementation

By default, any class generated with gen-class will automatically load its implementing namespace from the classpath the first time it is used, just as if you had require'd the namespace. If you are using some alternative code loading mechanism and you do not want the generated class to interfere, add the :load-impl-ns false option to gen-class.

Namespace Declarations with gen-class

gen-class can appear as part of a namespace declaration in the ns macro. In this case, it is written as (:gen-class options...). Within ns, the :name and :impl-ns options default to the namespace being declared and :main defaults to true. Everything else is the same. However, remember that you need not limit yourself to one namespace per generated class. You could generate several classes, with different :prefix options, and put all the method implementations in the same Clojure namespace.

Simple Command-Line Program

If all you need is a program that can be run at the command line, you only need a -main function and an ns declaration containing (:gen-class), as in this example:

(ns com.example.app
  (:gen-class))

(defn -main [& args]
  (println "Hello, World!"))

When AOT-compiled into a directory named classes, this example can be run with the command:

java -cp classes:clojure.jar com.example.app

Summary

Clojure is not intended to replace the Java language. Rather, it is designed to augment the capabilities of the Java platform with a different style of programming. Newcomers to Clojure may dislike the intrusion of Java class and method names into their Clojure code, and rush to wrap every Java method call in a Clojure function. More experienced Clojure programmers appreciate the power offered by Java libraries and are comfortable mixing Java methods and Clojure functions. The world is too big to implement everything from scratch. Clojure takes advantage of the vast ecosystem of Java libraries and lives comfortably in a Java-based environment.



[6] http://java.sun.com/docs/books/tutorial/reflect/

[7] Method names as first-class functions has been suggested for a future version of Clojure.

[8] In contrast, the .NET Common Language Runtime has strongly-typed generics, which are more difficult to implement in a dynamically-typed language like Clojure. This was one reason for the choice of Java as the primary platform for Clojure.

[9] Java Classloaders, however, permit you to create multiple, independent execution contexts within a single JVM. Classloaders are an advanced Java topic outside the scope of this book.

[10] Early JVMs were implemented as bytecode interpreters. Modern JVM implementations use just-in-time compilation to convert the platform-independent Java bytecode into optimized machine code.

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

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