clojureclojure-java-interop

Calling Clojure from Java: Why is the "new" style (clojure.java.api.Clojure) better than the "old" one (gen-class)?


Something is puzzling me after reading the this great answer to a related question:

There are two possibilities to share a function that I wrote in Clojure with Java developers

Why is the second approach "the better one"?


Solution

  • You are sorta setting up a false dichotomy here. Every approach involves creating a jar file: that is just how JVM programs are distributed. But there are 3 different ways for Java code to invoke Clojure code contained in a jar:

    1. Use methods in clojure.lang.RT to initialize the runtime, load files, and then look up vars. This is the old, deprecated approach.
    2. Use methods in clojure.java.api.Clojure to look up functions and invoke them. This is the newer version of (1), and hides some of the messy stuff you could accidentally get wrong.
    3. Use gen-class in the Clojure library to define a more Java-friendly interface to the Clojure functions.

    You can still do (3) - there's nothing wrong with it exactly. But gen-class is a pretty clunky tool, and except for the simplest examples like exposing a number of static methods, it's just not a lot of fun, and it's not easy to provide an API that "feels" like a Java API using Clojure.

    But you know what's great at providing an API that feels like Java? Java! So what I recommend if you want to make a Clojure library easy to use in Java is to include some Java code in your Clojure library. That Java code, written by you, bridges the language gap. It accesses your Clojure code by mechanism (2) above, and presents a Java-friendly facade so the outside world doesn't have to know there's Clojure underneath.

    amalloy/thrift-gen is an example of a library I wrote years ago following this approach. It would not be at all easy to write this in pure Clojure, just because traditional Java idioms are very foreign to Clojure, and it doesn't support them all very well. By writing my own Java shim instead, Java clients get a very comfortable interface to work with, and I can just write Clojure that feels like Clojure instead of a bunch of gen-class nonsense.