Search code examples
clojuregen-class

Extending with :gen-class a class that exposes its naked fields


Suppose there is a Java class that doesn't provide getters and setters for all its fields, and I have to extend it with :gen-class and fiddle with them.

How do I access the superclass fields?

The quickest (and perhaps cleanest...) solution that comes to my mind right now is to create a java class that extends my super class, and extend it instead, but I was wondering if there is an alternative that sounds more direct.

Thanks!


Solution

  • The methods in generated classes can access base class fields with the help of the :exposes option of gen-class. :exposes expects a map where keys are symbols matching base class field names; values are also maps like {:get getterName, :set setterName}. Clojure generates those getter and setter methods automatically. They can be used to read and modify base class fields. This is documented in the docstring for gen-class.

    This approach works for public and protected fields. It does not work for private fields.

    Assuming the Java base class like this:

    package fields;
    
    class Base {
        public String baseField = "base";
    }
    

    The Clojure code to generate a subclass would be:

    (ns fields.core
      (:gen-class
       :extends fields.Base
       :methods [[bar [] String]
                 [baz [String] Object]]
       :exposes { baseField { :get getField :set setField }}))
    
    (defn -bar [this]
      (str (.getField this) "-sub"))
    
    (defn -baz [this val]
      (.setField this val)
      this)
    
    (defn -main
      [& args]
      (println (.. (fields.core.) (bar)))
      (println (.. (fields.core.) (baz "new-base") (bar))))
    

    Assuming all this is AOT compiled and ran, the output is:

    base-sub
    new-base-sub