Calling Custom Clojure Functions from Java

Calling Clojure APIs from Java is discussed in my first tutorial. This tutorial explores calling your own Clojure functions from Java. A common use case for this is when you write a JavaFX UI and want to have the controller code written in Clojure.

Our first example explains how to call a simple Clojure function that takes no arguments and returns no values. (Technically, all Clojure fuctions return values, but that doesn’t mean we care about all values Clojure functions return.)

(ns java-clj-interop.core)

(defn hello-world
  "Yeah. You guessed it. This prints 'Hello World!'"
  []
  (println "Hello World!"))

Yes. That is a simple ‘Hello World’. It causes ‘Hello World!’ to print and nothing fancier than that. However, a similar function might open a valve, or modify a memory location on a graphics card, or mutate a matrix representing a complex model. Function calls without arguments and without return values are common place. So we’ll start there.

package net.javajdk.main;

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

public class Main {

  public static void main(String[] args) {
    
    // We use Clojure's require function to load the 
    // Clojure file containing our hello-world function
    IFn require = Clojure.var("clojure.core", "require");
    require.invoke(Clojure.read("java-clj-interop.core"));
    
    // Then we call our hello-world function from Java
    IFn helloWorld = Clojure.var(
        "java-clj-interop.core", 
        "hello-world");
    
    helloWorld.invoke();

  }
}

You’ll notice that the Java code for this example takes care of loading the Clojure script, but it does so with a call to the Clojure language. That is how we get around the use of Clojure’s RT class for loading Clojure files. Remember, RT bad, IFn and Clojure good!

For more complex applications, you may want to use a Plain Old Java Object (POJO) to pass data around. For example, the next set of files populates a POJO with some data. The POJO (our data object) is passed to Clojure for handling. Then, the Java end of the application pulls the result out of the POJO it passed into Clojure.

First, here’s the POJO we are using as a data object. It consists of the private attributes and the necessary getters and setters to populate and view those attributes. It is nothing more than a container for passing data around.

package net.javajdk.main;

public class Pojo {
  
  private long num = 0;
  
  private long result;
  
  public long getNum() {
    return num;
  }
  
  public void setNum(long num) {
    this.num = num;
  }
  
  public long getResult() {
    return result;
  }
  
  public void setResult(long result) {
    this.result = result;
  }
  
}

Second, here’s the Main Java class we’re calling Clojure from. It uses Clojure’s require function to load the java-clj-interop.core file. That gives us access to the calculate function in that Clojure file. Remember, your Clojure file(s) and the Clojure JAR need to be on your class path for this to work. 

After loading the necessary Clojure function into an IFn called calculate, we create a new Pojo instance and pass it to Clojure with a call to calculate.invoke(). The result of the Clojure function retrieved for pojo and printed out.

package net.javajdk.main;

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

public class Main {

  public static void main(String[] args) {
    
    // We use Clojure's require function to load the 
    // Clojure file containing our calculate function
    IFn require = Clojure.var("clojure.core", "require");
    require.invoke(Clojure.read("java-clj-interop.core"));
    
    // Then we call our calculate function from Java
    IFn calculate = Clojure.var(
        "java-clj-interop.core", 
        "calculate");
    
    // Plain old Java Object
    Pojo pojo = new Pojo();
    
    // calling the Clojure calculate function
    calculate.invoke( pojo );
    
    // display the results
    System.out.println( "result = " + pojo.getResult() );

  }
}

Third, here’s the Clojure code that is processing the result inserting the result into the POJO for the Java code to handle. Our Pojo instance is referenced through the x argument to the calculate function. Clojure handles typing dynamically. You’ll notice that both the setResult() method and getNum() methods on the Pojo instance are called from Clojure.

(ns java-clj-interop.core
  (:import [net.javajdk.main Pojo]))

(defn calculate
  "Find and set result value on Pojo instance."
  [x]
  
  ;; set the calculation result
  (.setResult x 
    
    ;; do the calculation
    (+ 
      
      ;; get the value from
      ;; the Pojo passed in
      ;; from Java
      (.getNum x) 
      
      10)))

And that is how you pass data back and forth from Java to Clojure and back to Java using a Plain Old Java Object. My next tutorials will give example for using Java objects as return values and performing callbacks to Java from your Clojure code. I’m also planning to put together a full detailed example of integrating Clojure with a JavaFX controller, so keep checking back.