Я пытаюсь изучить библиотеку Java с использованием Clojure в качестве рабочего языка. Библиотека (как обычно в Java) очень объектно-ориентирована и требует иерархии классов в клиентском коде. Я определил класс, унаследованный от библиотечного класса, с некоторыми дополнительными методами и данными, хранящимися в виде изменяемого словаря в поле state
:
(: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)
Затем я создал дочерний класс, унаследованный от моего 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 {})])
…
Но я получаю исключение нулевого указателя при вызове макроса (get-field this :library-object-one)
для экземпляра ChildClass
в методе -setSomeData
- поле, определенное :state
, не наследуется и в ключе нет ключа :library-object-one
словарь.
Быстрое и грязное исправление - это переопределение функции -init
в дочернем классе, например:
(defn -init []
[[] (ref {:library-object-one (LibraryObjectOne.)
:library-object-two (LibraryObjectTwo.)})])
(т.е. скопировать код инициализации из родительского класса). Но это ужасное нарушение принципа СУХОЙ. Есть ли способ наследовать состояние от родительского класса?
Я понимаю, что это вовсе не идиоматическая Clojure, а своего рода злоупотребление API :gen-class
, которое предоставляется только для целей взаимодействия. Возможно, мне не следует использовать наследование со своей стороны, и я должен реализовать полиморфизм не OOP-способом (например, путем изменения функций и значений, хранящихся в словаре state
). Если это правда, где я могу увидеть хорошие примеры такого подхода?