There are existing answers to similar questions, but they tend to use Maven, which is not an option for my project. Also, I have not found any which give concrete examples of the syntax you use to import at the repl, especially when the class is local as opposed to from the web.
I want to import a Java class into my Clojure project:
public class MyLocalClass1 {
int x;
String y;
public MyLocalClass1() {
this.x = 0;
this.y = "hello there";
}
public MyLocalClass1(int x, String y) {
this.x = x;
this.y = y;
}
public void setX(int x) {
this.x = x;
}
public void setY(String y) {
this.y = y;
}
public int getX() {
return x;
}
public String getY() {
return y;
}
}
I successfully built it in Idea as Java2Import.jar.
Here is project.clj, where I import the jar:
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.1"]]
:resource-paths ["/path/to/my/Java/project/Java2Import/artifacts/Java2Import/"]
:repl-options {:init-ns import-local-java-4.core})
However, when I try to import, I get errors:
import-local-java-4.core=> (import (Java2Import.Java2Import))
Syntax error macroexpanding clojure.core/import at (/tmp/form-init9909516591129619328.clj:1:1).
Java2Import.Java2Import - failed: #{(quote quote)} at: [:class :quoted-spec :quote] spec: :clojure.core.specs.alpha/quotable-import-list
() - failed: Insufficient input at: [:package-list :spec :classes] spec: :clojure.core.specs.alpha/package-list
Java2Import.Java2Import - failed: #{(quote quote)} at: [:package-list :quoted-spec :quote] spec: :clojure.core.specs.alpha/quotable-import-list
(Java2Import.Java2Import) - failed: simple-symbol? at: [:class :spec] spec: :clojure.core.specs.alpha/quotable-import-list
What am I doing wrong?
Clone this repo and look at the directory structure and also project.clj
The source code files look like so:
~/io-tupelo/clj-java-template > ls -1 **/*.{clj,java}
project.clj
src/clj/demo/core.clj
src/java/demo/Calc.java
test/clj/_bootstrap.clj
test/clj/tst/demo/core.clj
The test namespace shows the correct syntax:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.string :as str])
(:import [demo Calc]))
(dotest
(is= 5 (add2 2 3)) ; from src/clj/demo/core.clj
(is= 42 (Calc/add2 29 13)) ; from src/java/demo/Calc.java
)
Run the unit tests in test/clj/tst/demo/core.clj
:
~/io-tupelo/clj-java-template > lein clean ; lein test
Compiling 1 source files to /Users/alanthompson/io-tupelo/clj-java-template/target/default+test+test/class-files
lein test _bootstrap
-----------------------------------
Clojure 1.10.3 Java 17.0.2
-----------------------------------
lein test tst.demo.core
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
You don't need to compile the Java code into a JAR file. In face, it is much easier if you just use the Java source files.
You can also run in the REPL:
~/io-tupelo/clj-java-template > lein repl
Compiling 1 source files to /Users/alanthompson/io-tupelo/clj-java-template/target/default/class-files
demo.core=> (import '[demo Calc])
demo.Calc
demo.core=> (prn :result (Calc/add2 6 7))
:result 13
nil
Please note that the function call syntax of require
and import
is different in the REPL than the macro syntax of the (ns ...)
form!
Also, any edits to your Java code will not be picked up in the REPL. You will need to exit and restart the REPL to force a Java recompile.
P.S.
As the README explains, I find it even better to use the lein-test-refresh
plugin. IMHO it is like a REPL on steroids!
P.P.S.
If you only have a JAR file (i.e. not Java source code), you may wish to use the lein install
command, which will copy the JAR file into the local Maven cache ~/.m2
where Leiningen can find it as normal.