Clojure Java Interop
Clojure provides seamless interoperability with Java, allowing you to leverage existing Java libraries and frameworks while writing idiomatic Clojure code.
Basic Java Interop
Creating Java Objects
(def date (java.util.Date.)) ; Constructor
(def list (java.util.ArrayList.)) ; Empty ArrayList
(def list2 (java.util.ArrayList. [1 2 3])) ; ArrayList with elements
Calling Methods
(.toUpperCase "hello") ; => "HELLO"
(.substring "clojure" 2) ; => "ojure"
(.getName String) ; => "java.lang.String"
Static Methods
(Math/sqrt 25) ; => 5.0
(System/currentTimeMillis)
Accessing Fields
(.-x (java.awt.Point. 10 20)) ; => 10
(.-MAX_VALUE Integer) ; => 2147483647
Importing Java Classes
Single Import
(import 'java.util.Date)
(def now (Date.))
Multiple Imports
(import '(java.util Date Calendar)
(import '(java.awt Point Rectangle))
ns Declaration
(ns my.app
(:import [java.util Date Calendar]
[java.awt Point Rectangle]))
Working with Java Collections
Converting Between Collections
(def jlist (java.util.ArrayList. [1 2 3]))
(def clist (seq jlist)) ; Convert to Clojure seq
(def jlist2 (java.util.ArrayList. clist)) ; Back to Java list
Using Java Collections
(def hashmap (java.util.HashMap.))
(doto hashmap
(.put "a" 1)
(.put "b" 2))
(seq hashmap) ; => (["a" 1] ["b" 2])
Exception Handling
try-catch
(try
(/ 1 0)
(catch ArithmeticException e
(println "Caught exception:" (.getMessage e)))
finally
(try
(some-risky-operation)
(catch Exception e
(handle-error e))
(finally
(clean-up)))
Implementing Java Interfaces
Anonymous Implementation
(def runnable
(reify Runnable
(run [this]
(println "Running"))))
(.start (Thread. runnable))
Implementing Multiple Interfaces
(def listener
(reify
java.awt.event.ActionListener
(actionPerformed [this e]
(println "Action performed"))
java.lang.Runnable
(run [this]
(println "Running"))))
Extending Java Classes
(def my-frame
(proxy [javax.swing.JFrame] []
(paint [g]
(proxy-super paint g)
(.drawString g "Hello" 50 50))))
Java Arrays
Creating Arrays
(def int-array (int-array [1 2 3]))
(def str-array (into-array String ["a" "b" "c"]))
(def multi-array (make-array Integer/TYPE 2 3))
Working with Arrays
(aget int-array 1) ; => 2
(aset int-array 1 99) ; Set element
(alength int-array) ; => 3
(seq int-array) ; => (1 99 3)
Advanced Interop
Varargs Methods
; Java: void printf(String format, Object... args)
(.printf System/out "%s %d" (into-array Object ["value:" 42]))
Primitive Types
(defn sum [^long a ^long b]
(+ a b))
Annotations
(defn ^{Deprecated true} old-function []
"This is deprecated")
Calling Clojure from Java
Loading Clojure Code
// Java code
import clojure.java.api.Clojure;
import clojure.lang.IFn;
public class ClojureCaller {
public static void main(String[] args) {
IFn require = Clojure.var("clojure.core", "require");
require.invoke(Clojure.read("my.clojure.ns"));
IFn cljFunction = Clojure.var("my.clojure.ns", "my-function");
Object result = cljFunction.invoke("arg1", 42);
}
}
Generating Java Classes
(ns my.genclass
(:gen-class
:name my.Person
:implements [java.lang.Runnable]
:methods [[getName [] String]
[setName [String] void]))
Performance Considerations
- Use type hints to avoid reflection
- Prefer Clojure collections for Clojure code
- Consider Java arrays for performance-critical numeric code
- Be mindful of boxing/unboxing with primitives
Common Java Libraries
java.util.concurrent
(def exec (java.util.concurrent.Executors/newFixedThreadPool 4))
(.submit exec #(println "Running task"))
java.nio
(def path (java.nio.file.Paths/get "file.txt" (into-array String [])))
(def lines (java.nio.file.Files/readAllLines path))
java.time
(def now (java.time.LocalDateTime/now))
(def tomorrow (.plusDays now 1))
Next Steps
Continue learning with:
- Clojure Spec - Data validation and specification
- Web Development - Building web applications