clojureleiningenclojure-java-interop

How to import local Java class at Clojure REPL?


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?


Solution

  • 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.