Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create multiple Java member variables with Clojure's gen-class

This answer to a very old question about Clojure-Java interop explains how to use gen-class with the :state and :init keywords to create a single public instance variable accessible from Java. This is enough if you only need one piece of data to be available to Java classes, or if you can require the Java classes to use accessor functions that read, for example, a map stored in the state variable. This method also allows the data to change, e.g. by storing atoms in the state variable.

What if I want to create more than one instance variable that's directly readable in a Java class? Is this possible? For example, I can compile the following files and execute the Bar class, and see the value 42 of foo.bar printed out.

Foo.clj:

(ns students.Foo
  (:gen-class
    :name students.Foo
    :state bar
    ; :state baz
    :init init))

(defn -init
  []
  [[] 42])

Bar.java:

package students;

public class Bar {

    public static void main(String[] args) {
        Foo foo = new Foo();
        System.out.println(foo.bar);
        // System.out.println(foo.baz);
    }
}

If I uncomment the baz lines, Bar.java won't compile--the compiler seems to randomly create either bar or baz as the state variable for Foo, so only one of them is available to Bar. And in any event, I don't see how to initialize both bar and baz using an init function.

like image 809
Mars Avatar asked May 01 '15 16:05

Mars


1 Answers

The gen-class macro does not support defining more than one public field. You have to use the defrecord macro or the deftype macro instead.

(defrecord Foo [bar baz])

Unfortunately, both the defrecord macro and the deftype macro does not prepare a way to define their constructors. So, where initializing multiple instance variables is mandatory, there is no shame in writing a Java class in Java.

like image 99
tnoda Avatar answered Oct 20 '22 14:10

tnoda