Define Java-visible classes from Clojure with gen-class when Java code must instantiate the type, call named methods, or rely on ordinary JVM class discovery.
gen-class is the interop tool for creating a named Java class from Clojure source. It is useful when the caller is Java-first: it wants a class name, constructors, methods, interfaces, and compiled bytecode rather than a Clojure var or function.
Do not use gen-class just to organize Clojure code. Start with namespaces, functions, records, protocols, reify, or proxy; add generated classes only when the Java side makes a named type part of the contract.
gen-classThe gen-class macro in Clojure is a tool that allows you to generate a Java class from Clojure code. This is particularly useful when you need to create classes that Java code can instantiate and interact with. Unlike Java, where classes are defined explicitly, Clojure uses gen-class to dynamically generate bytecode for the class at compile time.
gen-classgen-classTo define a class using gen-class, you need to specify several options that dictate the class’s behavior and structure. Here’s a basic example to illustrate the concept:
1(ns mynamespace.MyClass
2 (:gen-class
3 :name mynamespace.MyClass
4 :extends java.lang.Object
5 :implements [java.io.Serializable]
6 :constructors {[String] [String]}
7 :methods [[getName [] String]]))
8
9(defn -getName
10 "Returns the name of the instance."
11 [this]
12 "MyClass Instance")
Namespace Declaration: The ns macro declares the namespace, which is similar to a package in Java.
:gen-class Options:
:name: Specifies the fully qualified name of the class.:extends: Indicates the superclass. Here, it’s java.lang.Object.:implements: Lists interfaces the class implements, such as java.io.Serializable.:constructors: Maps constructor argument types to their corresponding parameter types.:methods: Lists methods with their signatures.Method Definition: The -getName function defines the method’s behavior. The this parameter refers to the instance of the class.
In Clojure, constructors are specified using the :constructors option in gen-class. This option maps constructor argument types to their parameter types. Here’s an example with a constructor:
1(ns mynamespace.Person
2 (:gen-class
3 :name mynamespace.Person
4 :constructors {[String int] [String int]}
5 :methods [[getName [] String]
6 [getAge [] int]]))
7
8(defn -init
9 "Constructor for Person class."
10 [name age]
11 [[name age] nil])
12
13(defn -getName
14 [this]
15 (first this))
16
17(defn -getAge
18 [this]
19 (second this))
-init Method: This method acts as the constructor. It returns a vector with the instance variables and nil for the superclass constructor.name and age are stored in a vector, which is accessed by other methods.To implement interfaces, list them in the :implements option. Here’s an example implementing java.lang.Comparable:
1(ns mynamespace.ComparablePerson
2 (:gen-class
3 :name mynamespace.ComparablePerson
4 :implements [java.lang.Comparable]
5 :constructors {[String int] [String int]}
6 :methods [[compareTo [Object] int]]))
7
8(defn -init
9 [name age]
10 [[name age] nil])
11
12(defn -compareTo
13 [this other]
14 (compare (second this) (second other)))
-compareTo Method: Implements the compareTo method from java.lang.Comparable. It compares the age of two ComparablePerson instances.Static methods can be defined using the :methods option with a static keyword. Here’s how you can define a static method:
1(ns mynamespace.StaticExample
2 (:gen-class
3 :name mynamespace.StaticExample
4 :methods [[^:static staticMethod [] String]]))
5
6(defn -staticMethod
7 []
8 "This is a static method.")
Once you’ve defined your class with gen-class, you can compile it and use it in Java like any other Java class. Here’s an example of how you might use the Person class from Java:
1import mynamespace.Person;
2
3public class Main {
4 public static void main(String[] args) {
5 Person person = new Person("Alice", 30);
6 System.out.println("Name: " + person.getName());
7 System.out.println("Age: " + person.getAge());
8 }
9}
To compile and run the Clojure code, you’ll need to use Leiningen or another build tool that supports Clojure. Here’s a basic setup using Leiningen:
lein new app myapp.project.clj includes necessary dependencies.lein compile to compile the Clojure code.Experiment with the gen-class macro by modifying the examples above. Try adding additional methods, implementing more interfaces, or creating more complex constructors. This hands-on approach will deepen your understanding of how Clojure and Java can work together seamlessly.
gen-classBelow is a diagram illustrating the flow of defining a class with gen-class and its interaction with Java code.
classDiagram
class ClojureNamespace {
+String name
+int age
+init(String, int)
+getName() String
+getAge() int
}
class JavaClass {
+main(String[] args)
}
ClojureNamespace <|-- JavaClass
Diagram Description: This diagram shows a Clojure namespace defining a class with gen-class, which is then used by a Java class. The Clojure class has methods getName and getAge, and a constructor init.
gen-class is a powerful tool for creating Java-compatible classes in Clojure.:methods and :constructors to define methods and constructors.:implements option.gen-class can be used in Java as if they were native Java classes.For more information on gen-class and Java interoperability, consider exploring the following resources:
gen-classgen-classgen-class that implements multiple interfaces and includes both instance and static methods.gen-class to extend a Java class and override one of its methods.By mastering gen-class, you’ll enhance your ability to integrate Clojure with Java, leveraging the strengths of both languages to build powerful applications.
gen-class in Clojure for Java Interoperability