简体   繁体   中英

Clojure: is it possible to inherit state of class defined with :gen-class?

I'm trying to study some Java library with Clojure as a working language. The library is (as usual in Java) very object-oriented and needs class hierarchies in client code. I've defined a class inherited from a library class with some additional methods and the data stored as a mutable dictionary in a state field:

(:gen-class
   :name my-project.my-parent-class.MyParentClass
   :extends com.example.library.LibraryClass
   :methods [[setSomeData [com.example.library.LibraryType] void]]
   :exposes-methods {libraryMethodOne parentLibraryMethodOne
                     libraryMethodTwo parentLibraryMethodTwo}
   :init init
   :state state))

(defmacro set-field!
  [this key value]
  `(dosync (alter (.state ~this) assoc ~key ~value)))

(defmacro get-field
  [this key]
  `(@(.state ~this) ~key))

(defn -init []
  [[]
   (ref {:library-object-one (LibraryObjectOne.)
         :library-object-two (LibraryObjectTwo.)})])

(defn -setSomeData [this t]
  (.setSomething (get-field this :library-object-one) t)

… ; (library methods overriding here)

I then created a child class inherited from my MyParentClass :

(:gen-class
   :name my-project.my-child-class.ChildClass
   :extends my-project.my-parent-class.MyParentClass
   :exposes-methods {libraryMethodOne myParentClassMethodOne}
   :init init
   :state state))

(defn -init []
  [[] (ref {})])
…

But I get a null pointer exception when I call (get-field this :library-object-one) macro for a ChildClass instance in a -setSomeData method — the field defined by :state is not inherited and there is no key :library-object-one in a dictionary.

Quick and dirty fix is redefine -init function in a child class like this:

(defn -init []
  [[] (ref {:library-object-one (LibraryObjectOne.)
            :library-object-two (LibraryObjectTwo.)})])

(ie to copy the initialization code from the parent class). But it's a terrible violation of the DRY principle. Is there a way to inherit a state from the parent class?

I understand it's not an idiomatic Clojure at all and kind of an abuse of the :gen-class API, which is provided for interoperability purposes only. Maybe I shouldn't use inheritance on my side and I must implement polymorphism in some non-OOP way (for example, by modifying functions and values stored in a state dictionary). If it is true, where can I see good examples of this approach?

You don't have to provide a :state for the subclasses. If you don't, it will just call the parent's method.

(ns my-project.classes)

(gen-class
  :name my_project.my_parent_class.MyParentClass
  :init init
  :state state)

(defn -init []
      [[]
       (ref {:library-object-one "foo"
             :library-object-two "bar"})])

(gen-class
  :name my_project.my_child_class.ChildClass
  :extends my_project.my_parent_class.MyParentClass)

And the calling namespace:

(ns my-project.core
  (:import (my_project.my_child_class ChildClass))
  (:gen-class))

(defn -main [& args]
  (let [inst (ChildClass.)]
    (println @(.state inst))))

This prints:

{:library-object-one foo, :library-object-two bar}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM