1

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?

James Strieter
  • 775
  • 3
  • 10
  • What is your ultimate goal here? Do you _want_ to build a jar with Idea and use with Leiningen (bad idea IMHO). Or would your rather build that java project, deploy a lib, use that lib with leiningen? Or use those java files directly inside from your clojure project? – cfrick Apr 21 '22 at 21:05
  • I wanted to do the first option. What makes that a bad idea? – James Strieter Apr 21 '22 at 22:20
  • The proximate cause of your error is that you are using the wrong syntax for `import`. Either use `(import packagename.classname)` or `(import (packagename classname))`. You are using `(import (packagename.classname))` which results in the error you shared. – dorab Apr 22 '22 at 00:31
  • 1
    The bad idea is, that you now need intellij to build your project. Might be perfectly fine for one-person-throw-away-project. For any degree of reproducible build (share it with others, build in CI, ...) this is basically a no-go. – cfrick Apr 22 '22 at 05:10
  • I wound up adding a directory in the src directory called java2import, and put the java files in there with package name java2import. (In other words I abandoned the local jar.) Seems to me that if java2import is its own git repo, and the Clj repo knows java2import is a sub-repo, that would solve the reproducible builds problem right? – James Strieter Apr 22 '22 at 12:53
  • @cfrick I took stuff from your comments and the answer and put them in https://github.com/jstr045329/import-local-java-4. Feedback welcome – James Strieter Apr 22 '22 at 13:25
  • I'd use a src/clj and a src/java directory to also keep the packate-to-directory structure for java correct. Or whatever is not confusing for your IDE. – cfrick Apr 22 '22 at 13:48
  • It is crucial that you [set the :java-source-paths](https://stackoverflow.com/a/11884604/1008794) value in your Leiningen `project.clj` file to point at the directory where you have your Java source code. – Rulle Apr 22 '22 at 14:06

1 Answers1

2

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.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • 3
    Finally, a question where I agree your template project is a good thing to include in the answer! – amalloy Apr 22 '22 at 02:29
  • I looked at your project and followed the directory structure to make my own. Any chance you could add the import statement in core.clj to show the correct syntax for a source file? – James Strieter Apr 22 '22 at 12:50
  • I took stuff from the answer and comments and put them together in https://github.com/jstr045329/import-local-java-4. Feedback welcome – James Strieter Apr 22 '22 at 13:26