Calling Clojure code from Java

I’ve now worked through a few different methods for calling Clojure functions from Java. Since none of the guides I found go through some of the important details, I decided to post my own here, both so I remember how to do this in the future and for any others interested. Let me know if you try this out, or have questions!

There are two ways I’ve found useful for calling Clojure functions. The first has the Clojure code in the same project as the Java code. The second is if you have a large Clojure project (like Clojush) and you want to be able to call a few of its functions.

Method 1: Same Project

This method allows you to put both Clojure and Java files in the same project, and call one from the other. It does not require you to create a jar of your Clojure code every time you change it (nice), but all of the code is in the same project. It’s probably the easier way unless you’re working with 2 projects.

This assumes you started a Java project. It also works with Clojure projects (started with lein), but slightly differently. I’ll point out the differences below.

####1.
Create your Clojure and Java files. Write some functions in the Clojure file. My example has the files:

mycoj/dashed_namespace/app/core.clj
mycoj/dashed_namespace/app/Main.java

where the Clojure file has the namespace mycoj.dashed-namespace.app.core and the Java file has the package package mycoj.dashed_namespace.app;.

####2.
(This step isn’t necessary if you have a Clojure project, since lein will take care of the Clojure dependency.)

Download the latest stable release of Clojure. Unzip it and find the file clojure-x.y.z.jar. Copy this file into your project somewhere (maybe a lib/ directory). This jar needs to be on your classpath somewhere. This is how I did it in Eclipse: find the file in your Eclipse project navigator, right click > Build Path > Add To Build Path. I’m sure there are other ways for other IDEs.

####3.
In your Java file, import the following:

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

####4.
Add some code along the lines of the following to call the Clojure functions, where hello-world and cube are functions in my Clojure file:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("mycoj.dashed-namespace.app.core"));

Clojure.var("mycoj.dashed-namespace.app.core", "hello-world").invoke();
            
IFn myCube = Clojure.var("mycoj.dashed-namespace.app.core", "cube");
long cubed5 = (Long) myCube.invoke(5);
System.out.println(cubed5);

The first two lines get Clojure’s require function and call it on the namespace of the Clojure file, so that you can use its functions. The third line “invokes” the hello-world function, which just prints something. The last 3 lines define a IFn object myCube, call the function with argument 5, and print the result.

Method 2: Uberjar of Clojure Project

In this method, you create an uberjar of a Clojure project, marking one or more namespaces as classes and one or more functions as methods. This method is useful if you want to keep your Clojure and Java projects separate.

####1.
Put the following into your project.clj:

:aot [the.namespace.to.make.into.class]`

This will make it so the namespace is turned into a class.

####2.
Put this in your namespace:

    (:gen-class
        :name the.namespace.to.make.into.class
        :methods [#^{:static true} [generate_data_files [String int String] String]])

Here, the :name will be the name of the Java class, and can’t have dashes. This doesn’t have to be the same as the namespace. :methods should be a vector of the functions to turn into methods. Each method has a name, a vector of parameter types, and an output type – in this example, there are 3 inputs (String int String) and an output String.

####3.
For each method you declared in the namespace, you need a function with that name that additionally begins with a dash. So, in my example above, I should define a function -generate_data_files that takes 3 arguments and returns a String.

####4.
Now, you are ready to make an uberjar. At the command line, type lein uberjar. This will make two jar files in the Project/target. You want the one with “standalone” in its name.

####5.
Copy the “standalone” jar file into your Java project. This jar needs to be on your classpath somewhere. This is how I did it in Eclipse: find the file in your Eclipse project navigator, right click > Build Path > Add To Build Path. I’m sure there are other ways for other IDEs.

####6.
In your Java file, import the class using the class name from step 2. For example:

import clojush.problems.software.tcdg;

####7.
Now, you can call the function (now a method) in your Java program; something like this:

String location = tcdg.generate_data_files("median", 4, "csv");

Notice that you don’t need the dash on the function name, and that the arguments match the parameter types given in Clojure.

3 Likes