Я пытаюсь изучить некоторую библиотеку 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.)})])
(т.е. скопировать код инициализации из родительского класса). Но это ужасное нарушение принципа DRY. Есть ли способ наследовать состояние от родительского класса?
Я понимаю, что это вовсе не идиоматический Clojure, а своего рода злоупотребление :gen-class
API, которое предоставляется только для целей взаимодействия. Возможно, мне не следует использовать наследование на своей стороне, и я должен реализовать полиморфизм каким-либо не-ООП-способом (например, путем изменения функций и значений, хранящихся в словаре state
). Если это правда, где я могу увидеть хорошие примеры такого подхода?
[[] (nth (my-project.my-parent-class/-init) 1)]
из функции-init
дочернего класса. Но это все еще выглядит как кладж для меня. - person Yuriy Al. Shirokov   schedule 06.11.2018