Data Conversion at Java Boundaries
Learn where Java values should be converted into idiomatic Clojure data, when to keep Java representations such as arrays or collections, and how to make null, primitive, and mutable-container boundaries explicit instead of letting them leak through the program.
Interop usually fails, or becomes ugly, at the type boundary: null vs nil, Java arrays vs Clojure sequences, mutable collections vs persistent data, and primitive-friendly Java APIs vs generic Clojure values.
A practical rule for Java engineers:
Convert once, at the edge. Keep the inside of your program on one side of the boundary.
Common conversions you will see:
- Java collections ↔ Clojure maps/vectors (
into, seq, vec)
- arrays ↔ seqs (
into-array)
null ↔ nil (be explicit and defensive)
This section helps you build consistent conventions so interop code stays predictable and reviewable.
In this section
-
Primitive Types and Boxing
Use primitive-aware Java interop deliberately: know when Clojure's generic numeric model is enough, when boxing is harmless, and when type hints, primitive arrays, or Java method signatures make representation visible for correctness or performance.
-
Java Collections at Clojure Boundaries
Convert, view, and pass Java collections without hiding their mutability. Treat Java List, Set, and Map values as boundary representations, then decide deliberately whether the Clojure side should keep them, wrap them, or copy them into persistent data.
-
Java Arrays in Clojure
Use Java arrays only where they earn their place: API signatures, primitive-heavy code, or low-level interop. Create, read, mutate, and convert arrays while keeping mutable array handling at the edge of otherwise data-oriented Clojure code.
-
Java null and Clojure nil
Translate Java null habits into explicit Clojure absence handling. Use nil checks, defaults, validation, and boundary conversion so Java APIs can still return missing values without spreading NullPointerException thinking through the Clojure core.