Here are the ways to subclass a your custom Java class from Clojure. I have been fiddling to get this to work with lein
for some time now. In your lein project say you have Java source at src/java
. You want to extend a class from clojure, call super class method etc. There are two ways to extend, proxy and gen-class.
It's important to note that you need to have a package structure for the Java classes. Naked classes don't work because it's getting qualified to java.lang
namespace when you try to run giving a ClassNotFoundException
.
// src/java/com/example/BaseClass.java
package com.example;
public class BaseClass {
public String greet() {
return "Hello from BaseClass";
}
}
You need to specify the Java source location using :java-source-paths
in project.clj
. If you are using :gen-class
to extend, then you need to AOT compile your Clojure file. When running project with :java-source-paths
added under Windows, I am getting the following error even if JAVA_HOME
is set to JDK location and bin
is in path.
Java compiler not found; Be sure to use java from a JDK
rather than a JRE by modifying PATH or setting JAVA_CMD.
Adding JAVA_CMD
env variable to point to java_home-path\bin\java
works.
; project.clj
(defproject subclass "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.8.0"]]
:java-source-paths ["src/java"]
:main subclass.core
:target-path "target/%s"
:aot [subclass.core]
:profiles {:uberjar {:aot :all}})
Extending using :gen-class
Extending using :gen-class
uses :extends
keyword. If you want to call a super class method, use :expose-methods
to specify an alias under which that method will be available locally. Here the greet
from BaseClass
is locally referred as pgreet
; src/subclass/core.clj
(ns subclass.core
(:gen-class
:extends com.example.BaseClass
:exposes-methods {greet pgreet}))
;; greet method override. gen-class prefixes generated classes with '-' by default.
(defn -greet [this]
;this arg is the object of this class, i.e., subclass.core.
(.pgreet this) ;calls super class' greet()
(println "hi from clj"))
(defn -main [& args]
(.greet (subclass.core.)))
Running the above gives:
$ lein run
subclass.core=> (-main)
Hello from BaseClass
hi from clj
nil
Extending using proxy
; src/subclass/core.clj
(ns subclass.core
(:import com.example.BaseClass))
(defn my-greet []
(proxy [BaseClass] [] ; the class to extend
; override greet()
(greet []
(proxy-super greet) ; call super class method
(println "hi from my-greet"))))
(defn -main [& args]
(.greet (my-greet))) ; calling my-greet returns a proxy object. Invoke greet method on it.
Running the proxy version gives:
$ lein repl
subclass.core=> (-main)
Hello from BaseClass
hi from my-greet
nil
Using proxy
or gen-class
for extending depends on your use case.