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.)})])
(i.e. 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}