Calling Clojure Functions from Java

Mixing Clojure functions and Java methods usually happens when pure Clojure applications need to interact with Java libraries. However, sometimes you have Clojure functions receiving events from a Java program. This is often the case when you have a Java program with a JavaFX, Java Swing, or Java AWT GUI, but you want all the controller logic written in Clojure. It certainly is  possible to write Clojure code calling Java libraries to create a decent UI, but doing so would be clunky at best.

It is also possible that you have a legacy Java application that you want to write Clojure plugins for. This is easily accomplished by calling Clojure functions from any appropriate place in the Java code. You can call directly into uncompiled Clojure scripts. To call Clojure scripts, you just need to know the right classes for loading your code.

You could precompile your Clojure scripts (thus making calling the Clojure functions similar to calling any Java API), but precompiling Clojure scripts limits the use of some dynamic features that make Clojure so powerful. I hate the thought of crippling Clojure for convenience. I recommend never precompiling Clojure scripts, because doing so limits the future uses of your Clojure without a major refactoring of your Java to Clojure interfaces.

In its simplest form, calling Clojure functions from Java involves populating an IFn object with a reference to a Clojure function and then invoking the Clojure function. For example, the following code calls the clojure function ‘-‘. Java programmers might not thing of minus as a function, but in Clojure it is.

package net.javajdk.main;

import clojure.java.api.Clojure;
import clojure.lang.IFn;

public class Main {

  public static void main(String[] args) {
    
    int a = 10;
    int b = 1;
    
    IFn minus = Clojure.var("clojure.core", "-");
    long result = (Long) minus.invoke(a, b);
    System.out.println("a - b = " + result);

  }

}

You need a clojure JAR in your class path to run this program. Don’t use the slim JAR, unless you have serious space or bandwidth issues. Instead, use the full version of the Clojure JAR. The Clojure JAR contains the clojure.java.api.Clojure class and the clojure.lang.IFn class.

The Strings passed to the var() method of the Clojure class are first, “clojure.core”, and second “-“. “clojure.core” is the name space that the “-” method is found in. From a Java perspective, the package is “clojure”, the class file is “core”, and the method is “-“. That’s not strictly true, but it makes it easier for Java programmers to think of it that way.

The “-” function gets wrapped in the IFn class. The “-” function then can be called with a call to the invoke() method with the proper arguments for the “-” function.

A common mistake is to mess up the capitalization of the IFn class. Note that only the ‘n’ is lowercase.

Run your program as a Java application, and you should get a printout reading “a – b = 9”. Notice that the only two imports from Clojure are clojure.java.api.Clojure and clojure.lang.IFn. It is recommended that you only use these two classes when interacting with Clojure from the Java side. All other classes may have implementation details change making your code incompatible with future Clojure updates. Direct calls to clojure.lang.RT from Java (and other internally used Clojure classes) is a bad idea. However, you can use any of the standard Clojure functions from inside of your Clojure scripts or through calls to Clojure and IFn, so this restriction isn’t as limiting as it might first sound.

One of the first questions you’ll ask if you start calling standard Clojure functions from Java is, How do I figure out the return type to cast the IFn invoke() call to? Now, I could get all professor-like and say, these types of call always return such-and-such objects, but let’s not even go there. If you don’t know what to expect from a Java call to a Clojure function, just ask Java to tell you, as in the following example.

package net.javajdk.main;

import clojure.java.api.Clojure;
import clojure.lang.IFn;

public class Main {

  public static void main(String[] args) {
    
    int a = 10;
    int b = 1;
    
    IFn minus = Clojure.var("clojure.core", "-");
    String result = minus.invoke(a, b).getClass().getName();
    
    System.out.println( result );

  }

}

As you can see by running this code, the return is a java.lang.Long, and those are easy to deal with.

package net.javajdk.main;

import clojure.java.api.Clojure;
import clojure.lang.IFn;

public class Main {

  public static void main(String[] args) {
    
    int a = 42;
    int b = 42;
    
    IFn equals = Clojure.var("clojure.core", "==");
    String className = equals.invoke(a, b).getClass().getName();
    
    System.out.println( className );

  }

}

The above code produces a java.lang.Boolean.

Using the core Clojure API’s are great for examples, but in the real world, you’ll want to call your own Clojure functions from Java. That is the topic of the next Java Clojure interop tutorial.