176

Most of the top google hits for "calling clojure from java" are outdated and recommend using clojure.lang.RT to compile the source code. Could you help with a clear explanation of how to call Clojure from Java assuming you have already built a jar from the Clojure project and included it in the classpath?

dskrvk
1,38817 silver badges24 bronze badges
asked Feb 2, 2010 at 3:51
3
  • 8
    I don't know that compiling the source each time is "outdated" as such. It's a design decision. I'm doing that now as it makes integrating Clojure code into a legacy Java Netbeans project a snap. Add Clojure as a library, add a Clojure source file, setup the calls, and instant Clojure support without having multiple compilation/linking steps! At the cost of a fraction of a second delay on each app start. Commented Dec 28, 2011 at 14:49
  • See latest for Clojure 1.6.0. Commented May 9, 2014 at 3:18
  • 2
    See latest for Clojure 1.8.0 — Clojure now has compiler direct linking. Commented Feb 15, 2016 at 21:18

10 Answers 10

179

Update: Since this answer was posted, some of the tools available have changed. After the original answer, there is an update including information on how to build the example with current tools.

It isn't quite as simple as compiling to a jar and calling the internal methods. There do seem to be a few tricks to make it all work though. Here's an example of a simple Clojure file that can be compiled to a jar:

(ns com.domain.tiny
 (:gen-class
 :name com.domain.tiny
 :methods [#^{:static true} [binomial [int int] double]]))
(defn binomial
 "Calculate the binomial coefficient."
 [n k]
 (let [a (inc n)]
 (loop [b 1
 c 1]
 (if (> b k)
 c
 (recur (inc b) (* (/ (- a b) b) c))))))
(defn -binomial
 "A Java-callable wrapper around the 'binomial' function."
 [n k]
 (binomial n k))
(defn -main []
 (println (str "(binomial 5 3): " (binomial 5 3)))
 (println (str "(binomial 10042 111): " (binomial 10042 111)))
)

If you run it, you should see something like:

(binomial 5 3): 10
(binomial 10042 111): 49068389575068144946633777...

And here's a Java program that calls the -binomial function in the tiny.jar.

import com.domain.tiny;
public class Main {
 public static void main(String[] args) {
 System.out.println("(binomial 5 3): " + tiny.binomial(5, 3));
 System.out.println("(binomial 10042, 111): " + tiny.binomial(10042, 111));
 }
}

It's output is:

(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

The first piece of magic is using the :methods keyword in the gen-class statement. That seems to be required to let you access the Clojure function something like static methods in Java.

The second thing is to create a wrapper function that can be called by Java. Notice that the second version of -binomial has a dash in front of it.

And of course the Clojure jar itself must be on the class path. This example used the Clojure-1.1.0 jar.

Update: This answer has been re-tested using the following tools:

  • Clojure 1.5.1
  • Leiningen 2.1.3
  • JDK 1.7.0 Update 25

The Clojure Part

First create a project and associated directory structure using Leiningen:

C:\projects>lein new com.domain.tiny

Now, change to the project directory.

C:\projects>cd com.domain.tiny

In the project directory, open the project.clj file and edit it such that the contents are as shown below.

(defproject com.domain.tiny "0.1.0-SNAPSHOT"
 :description "An example of stand alone Clojure-Java interop"
 :url "http://clarkonium.net/2013/06/java-clojure-interop-an-update/"
 :license {:name "Eclipse Public License"
 :url "http://www.eclipse.org/legal/epl-v10.html"}
 :dependencies [[org.clojure/clojure "1.5.1"]]
 :aot :all
 :main com.domain.tiny)

Now, make sure all of the dependencies (Clojure) are available.

C:\projects\com.domain.tiny>lein deps

You may see a message about downloading the Clojure jar at this point.

Now edit the Clojure file C:\projects\com.domain.tiny\src\com\domain\tiny.clj such that it contains the Clojure program shown in the original answer. (This file was created when Leiningen created the project.)

Much of the magic here is in the namespace declaration. The :gen-class tells the system to create a class named com.domain.tiny with a single static method called binomial, a function taking two integer arguments and returning a double. There are two similarly named functions binomial, a traditional Clojure function, and -binomial and wrapper accessible from Java. Note the hyphen in the function name -binomial. The default prefix is a hyphen, but it can be changed to something else if desired. The -main function just makes a couple of calls to the binomial function to assure that we are getting the correct results. To do that, compile the class and run the program.

C:\projects\com.domain.tiny>lein run

You should see output shown in the original answer.

Now package it up in a jar and put it someplace convenient. Copy the Clojure jar there too.

C:\projects\com.domain.tiny>lein jar
Created C:\projects\com.domain.tiny\target\com.domain.tiny-0.1.0-SNAPSHOT.jar
C:\projects\com.domain.tiny>mkdir \target\lib
C:\projects\com.domain.tiny>copy target\com.domain.tiny-0.1.0-SNAPSHOT.jar target\lib\
 1 file(s) copied.
C:\projects\com.domain.tiny>copy "C:<path to clojure jar>\clojure-1.5.1.jar" target\lib\
 1 file(s) copied.

The Java Part

Leiningen has a built-in task, lein-javac, that should be able to help with the Java compilation. Unfortunately, it seems to be broken in version 2.1.3. It can't find the installed JDK and it can't find the Maven repository. The paths to both have embedded spaces on my system. I assume that is the problem. Any Java IDE could handle the compilation and packaging too. But for this post, we're going old school and doing it at the command line.

First create the file Main.java with the contents shown in the original answer.

To compile java part

javac -g -cp target\com.domain.tiny-0.1.0-SNAPSHOT.jar -d target\src\com\domain\Main.java

Now create a file with some meta-information to add to the jar we want to build. In Manifest.txt, add the following text

Class-Path: lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar
Main-Class: Main

Now package it all up into one big jar file, including our Clojure program and the Clojure jar.

C:\projects\com.domain.tiny\target>jar cfm Interop.jar Manifest.txt Main.class lib\com.domain.tiny-0.1.0-SNAPSHOT.jar lib\clojure-1.5.1.jar

To run the program:

C:\projects\com.domain.tiny\target>java -jar Interop.jar
(binomial 5 3): 10.0
(binomial 10042, 111): 4.9068389575068143E263

The output is essentially identical to that produced by Clojure alone, but the result has been converted to a Java double.

As mentioned, a Java IDE will probably take care of the messy compilation arguments and the packaging.

answered Feb 2, 2010 at 20:27
Sign up to request clarification or add additional context in comments.

5 Comments

1. can i put your example on clojuredocs.org as example for the "ns" macro? 2. what is #^ in front of ":methods [#^{:static true} [binomial [int int] double]]" (i'm a newbie) ?
@Belun, sure you can use it as an example -- I'm flattered. The "#^{:static true}" attaches some metadata to the function indicating that binomial is a static function. It's required in this case because, on the Java side, we are calling the function from main - a static function. If binomial were not static, compiling the main function on the Java side would produce an error message about "non-static method binomial(int,int) cannot be referenced from a static context". There are additional examples on the Object Mentor site.
There is one crucial thing not mentioned here - to compile Clojure file to Java class, you need to: (compile 'com.domain.tiny)
how are you AOT compiling the Clojure source? This is where I'm stuck.
@MatthewBoston At the time the answer was written, I used Enclojure, a plugin for the NetBeans IDE. Now, I would probably use Leiningen, but has not tried or tested it.
139
+100

As of Clojure 1.6.0, there is a new preferred way to load and invoke Clojure functions. This method is now preferred to calling RT directly (and supersedes many of the other answers here). The javadoc is here - the main entry point is clojure.java.api.Clojure.

To lookup and call a Clojure function:

IFn plus = Clojure.var("clojure.core", "+");
plus.invoke(1, 2);

Functions in clojure.core are automatically loaded. Other namespaces can be loaded via require:

IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("clojure.set"));

IFns can be passed to higher order functions, e.g. the example below passes plus to read:

IFn map = Clojure.var("clojure.core", "map");
IFn inc = Clojure.var("clojure.core", "inc");
map.invoke(inc, Clojure.read("[1 2 3]"));

Most IFns in Clojure refer to functions. A few, however, refer to non-function data values. To access these, use deref instead of fn:

IFn printLength = Clojure.var("clojure.core", "*print-length*");
IFn deref = Clojure.var("clojure.core", "deref");
deref.invoke(printLength);

Sometimes (if using some other part of the Clojure runtime), you may need to ensure that the Clojure runtime is properly initialized - calling a method on the Clojure class is sufficient for this purpose. If you do not need to call a method on Clojure, then simply causing the class to load is sufficient (in the past there has been a similar recommendation to load the RT class; this is now preferred):

Class.forName("clojure.java.api.Clojure") 
answered May 9, 2014 at 3:03

3 Comments

Using this approach, the script cannot use quote '() and require things. Is there a solution for that?
I think there are only a couple special forms that don't also exist as vars. One workaround is to access it via Clojure.read("'(1 2 3"). It would be reasonable to file this as an enhancement request though to provide a Clojure.quote() or by making it work as a var.
@Renato There's no need to quote anything, because nothing is being evaluated by Clojure's evaluation rules anyway. If you want a list containing the numbers 1-3, then instead of writing '(1 2 3) you write something like Clojure.var("clojure.core", "list").invoke(1,2,3). And the answer already has an example of how to use require: it's just a var like any other.
35

EDIT This answer was written in 2010, and worked at that time. See Alex Miller's answer for more modern solution.

What kind of code are calling from Java? If you have class generated with gen-class, then simply call it. If you want to call function from script, then look to following example.

If you want to evaluate code from string, inside Java, then you can use following code:

import clojure.lang.RT;
import clojure.lang.Var;
import clojure.lang.Compiler;
import java.io.StringReader;
public class Foo {
 public static void main(String[] args) throws Exception {
 // Load the Clojure script -- as a side effect this initializes the runtime.
 String str = "(ns user) (defn foo [a b] (str a \" \" b))";
 //RT.loadResourceScript("foo.clj");
 Compiler.load(new StringReader(str));
 // Get a reference to the foo function.
 Var foo = RT.var("user", "foo");
 // Call it!
 Object result = foo.invoke("Hi", "there");
 System.out.println(result);
 }
}
answered Feb 2, 2010 at 8:09

3 Comments

To expand a little bit if you want to access def'd var in the namespace (i.e. (def my-var 10)) use this RT.var("namespace", "my-var").deref().
It doesn't work for me without adding RT.load("clojure/core"); at the beginning. What the strange behavior?
This works and is similar to what I've been using; not sure if it's the newest technique. I may be using Clojure 1.4 or 1.6, not sure.
13
+50

EDIT: I wrote this answer almost three years ago. In Clojure 1.6 there is a proper API exactly for the purpose of calling Clojure from Java. Please Alex Miller's answer for up to date information.

Original answer from 2011:

As I see it, the simplest way (if you don't generate a class with AOT compilation) is to use clojure.lang.RT to access functions in clojure. With it you can mimic what you would have done in Clojure (no need to compile things in special ways):

;; Example usage of the "bar-fn" function from the "foo.ns" namespace from Clojure
(require 'foo.ns)
(foo.ns/bar-fn 1 2 3)

And in Java:

// Example usage of the "bar-fn" function from the "foo.ns" namespace from Java
import clojure.lang.RT;
import clojure.lang.Symbol;
...
RT.var("clojure.core", "require").invoke(Symbol.intern("foo.ns"));
RT.var("foo.ns", "bar-fn").invoke(1, 2, 3);

It is a bit more verbose in Java, but I hope it's clear that the pieces of code are equivalent.

This should work as long as Clojure and the source files (or compiled files) of your Clojure code is on the classpath.

Felipe Augusto
8,22413 gold badges43 silver badges76 bronze badges
answered Jun 20, 2011 at 12:13

4 Comments

This advice is out of date as of Clojure 1.6 - please use clojure.java.api.Clojure instead.
Not a bad answer at the time, but I meant to bounty stackoverflow.com/a/23555959/1756702 as the authoritative answer for Clojure 1.6. Will try again tomorrow...
Alex' answer indeed deserves the bounty! Please let me know if I can help with transferring the bounty if needed.
@raek I don't mind that you got a little bonus from my over-caffeinated trigger finger. Hope to see you around the Clojure tag again.
10

I agree with clartaq's answer, but I felt that beginners could also use:

  • step-by-step information on how to actually get this running
  • information that's current for Clojure 1.3 and recent versions of leiningen.
  • a Clojure jar that also includes a main function, so it can be run standalone or linked as a library.

So I covered all that in this blog post.

The Clojure code looks like this:

(ns ThingOne.core
 (:gen-class
 :methods [#^{:static true} [foo [int] void]]))
(defn -foo [i] (println "Hello from Clojure. My input was " i))
(defn -main [] (println "Hello from Clojure -main." ))

The leiningen 1.7.1 project setup looks like this:

(defproject ThingOne "1.0.0-SNAPSHOT"
 :description "Hello, Clojure"
 :dependencies [[org.clojure/clojure "1.3.0"]]
 :aot [ThingOne.core]
 :main ThingOne.core)

The Java code looks like this:

import ThingOne.*;
class HelloJava {
 public static void main(String[] args) {
 System.out.println("Hello from Java!");
 core.foo (12345);
 }
}

Or you can also get all the code from this project on github.

answered Mar 29, 2012 at 17:58

1 Comment

Why did you use the AOT? Doesn't it cause the program to be not platform-independet anymore?
3

This works with Clojure 1.5.0:

public class CljTest {
 public static Object evalClj(String a) {
 return clojure.lang.Compiler.load(new java.io.StringReader(a));
 }
 public static void main(String[] args) {
 new clojure.lang.RT(); // needed since 1.5.0 
 System.out.println(evalClj("(+ 1 2)"));
 }
}
answered Nov 14, 2013 at 15:55

Comments

2

If the use case is to include a JAR built with Clojure in a Java application, I have found having a separate namespace for the interface between the two worlds to be beneficial:

(ns example-app.interop
 (:require [example-app.core :as core])
;; This example covers two-way communication: the Clojure library 
;; relies on the wrapping Java app for some functionality (through
;; an interface that the Clojure library provides and the Java app
;; implements) and the Java app calls the Clojure library to perform 
;; work. The latter case is covered by a class provided by the Clojure lib.
;; 
;; This namespace should be AOT compiled.
;; The interface that the java app can implement
(gen-interface
 :name com.example.WeatherForecast
 :methods [[getTemperature [] Double]])
;; The class that the java app instantiates
(gen-class
 :name com.example.HighTemperatureMailer
 :state state
 :init init
 ;; Dependency injection - take an instance of the previously defined
 ;; interface as a constructor argument
 :constructors {[com.example.WeatherForecast] []}
 :methods [[sendMails [] void]])
(defn -init [weather-forecast]
 [[] {:weather-forecast weather-forecast}])
;; The actual work is done in the core namespace
(defn -sendMails
 [this]
 (core/send-mails (.state this)))

The core namespace can use the injected instance to accomplish its tasks:

(ns example-app.core)
(defn send-mails 
 [{:keys [weather-forecast]}]
 (let [temp (.getTemperature weather-forecast)] ...)) 

For testing purposes, the interface can be stubbed:

(example-app.core/send-mails 
 (reify com.example.WeatherForecast (getTemperature [this] ...)))
answered Jan 8, 2016 at 15:35

Comments

0

Other technique that works also with other languages on top of JVM is to declare an interface for functions you want to call and then use 'proxy' function to create instance that implemennts them.

answered Feb 17, 2010 at 8:58

Comments

0

From Clojure i use gen-class, alternative is to write it from Java like bellow. I don't know if its the best way, but its simple.

package mypackage.clojure_interop;
import clojure.java.api.Clojure;
import clojure.lang.*;
public class Interop
{
 //reusable code,add your functions bellow
 //reads a clojure function from a namespace and it returns it as IFn
 public static IFn require = Clojure.var("clojure.core", "require");
 public static IFn getClojureFn(String ns, String fnName)
 {
 require.invoke(Clojure.read(ns));
 IFn fn = Clojure.var(ns, fnName);
 return fn;
 }
 //example using the above for keyword
 public static IFn keywordFn= getClojureFn("clojure.core","keyword");
 public static Object keyword(Object m)
 {
 return keywordFn.invoke(m);
 }
 //any other function will be the same like keyword example
 //IFn first and then a static method with the invoke
 //if many are needed and tedious it can be auto-generated also
}
answered Jan 18, 2024 at 23:40

Comments

-1

You can also use AOT compilation to create class files representing your clojure code. Read the documentation about compilation, gen-class and friends in the Clojure API docs for the details about how to do this, but in essence you will create a class that calls clojure functions for each method invocation.

Another alternative is to use the new defprotocol and deftype functionality, which will also require AOT compilation but provide better performance. I don't know the details of how to do this yet, but a question on the mailing list would probably do the trick.

answered Feb 2, 2010 at 17:07

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.