/img/clojure-banner.png

Implementing Java Interfaces with Deftype

Clojure's `deftype` macro allows for easy interop with Java interfaces

Written by: Alex Root-Roatch | Friday, August 30, 2024

Let's Get Some Java

One of my recent projects was to write a Clojure app using a Java app that I wrote as a library. This Java app follows the Command Pattern, which allows easy extension by implementing the Command interface without any need to modify any code inside the .jar file.

As Clojure runs on the JVM, it natively supports calling Java methods inside our Clojure applications. However, being a functional language, Clojure does not have classes, so we can't create a class that implements a Java interface. That's where deftype comes in.

Show the Code!

The implementation of an interface in Java looks something like this:

public interface Command {
  void execute();
}


public class MyClass implements Comand {
  public MyClass() {
  }

  public void execute() {
    // method body
  }
}

Implementing this in Clojure looks something like this:

(ns my-app 
    (:import MyJavaApp.Comand))
    
(deftype MyDeftype []
  Command
  (execute []
    ;method body
    ))    

Instead of an explicit constructor method, the parameters that would be fed to a constructor simply go in [] next to the name of the deftype. Instead of implements Command, we simply type Command on the second line to specify the interface or defprotocol being implemented.

If the execute method takes arguments, we will need to use this to implement them so Clojure knows those symbols are coming from the interface being implemented. For example:

(deftype MyDeftype []
  Command
  (execute [this param1 param2]
    ;method body
    ))    

Instantiating and Calling Methods

In Java, we create an instance of a class with:

MyClass coolClass = new MyClass();

In Clojure, we can either use a constructor of the factory method that Clojure creates under to hood:

;Constructor:
(def coolDeftype (MyDeftype.))

;Factory function
(def coolDeftype (->MyDeftype))

Then to call the execute method, we use an interesting blend of Clojure and Java syntax:

(.execute coolDeftype [params])

Conclusion

Deftypes give us an easy way to implement simple Java interfaces inside of Clojure. This allows us to bring in Java libraries and extend them in our Clojure app, after which we can write our entire app in Clojure.

Explore more articles

Browse All Posts