Reputation: 617
I want to call Clojure code compiled to a class from Java.
Clojure class:
(ns clj.core)
(gen-class
:name de.wt.TestClj
:state state
:init init
:prefix "-"
:main false
:methods [[setValue [String] void]
[getValue [] String]])
(defn -init []
[[] (atom {:value ""})])
(defn -setValue [this val]
(swap! (.state this) assoc :value val))
(defn -getValue [this]
(@(.state this) :value))
Compiled with lein uberjar
and copied the output -standalone.jar into the Java project under subdirs de/wt
.
The .java file:
import de.wt.TestClj;
class CljTest {
public static void main(String args[]) {
TestClj test = new TestClj();
System.out.println("Pre: " + test.getValue());
test.setValue("foo");
System.out.println("Post: " + test.getValue());
}
}
Now when I try to compile
~/testbed/cljava/java % tree [1 14:44:53]
.
├── CljTest.java
├── de
│ └── wt
│ └── TestClj.jar
└── TestClj.jar
2 directories, 3 files
~/testbed/cljava/java % javac -cp ./:./de/wt/TestClj.jar CljTest.java
CljTest.java:1: error: package de.wt does not exist
import de.wt.TestClj;
^
CljTest.java:5: error: cannot find symbol
TestClj test = new TestClj();
^
symbol: class TestClj
location: class CljTest
CljTest.java:5: error: cannot find symbol
TestClj test = new TestClj();
^
symbol: class TestClj
location: class CljTest
3 errors
What scheme do I need to follow when naming files/packages, creating directories and setting class paths?
Upvotes: 1
Views: 597
Reputation: 10865
You should be able to see the java class in your jar, something like:
$ jar tvf target/default-0.1.0-SNAPSHOT-standalone.jar | grep TestClj
2090 Mon Jan 01 18:43:12 CET 2018 de/wt/TestClj.class
If not, make sure you have an :aot
(ahead-of-time compilation) directive in your project.clj:
(defproject default "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:aot [clj.core]
:dependencies [[org.clojure/clojure "1.7.0"]])
Once you see the .class
file in your jar, with the jar in your classpath the import you have in your Java code should work fine.
As noted in the docs (https://clojure.org/reference/compilation):
Clojure compiles all code you load on-the-fly into JVM bytecode, but sometimes it is advantageous to compile ahead-of-time (AOT). Some reasons to use AOT compilation are:
To deliver your application without source
To speed up application startup
To generate named classes for use by Java
To create an application that does not need runtime bytecode generation and custom classloaders
Also see http://clojure-doc.org/articles/language/interop.html:
AOT
gen-class
requires ahead-of-time (AOT) compilation. It means that before using the classes defined withgen-class
, the Clojure compiler needs to produce .class files fromgen-class
definitions.
Upvotes: 2