Clojure: возможно ли наследовать состояние класса, определенного с помощью: gen-class? - PullRequest
0 голосов
/ 06 ноября 2018

Я пытаюсь изучить библиотеку 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). Если это правда, где я могу увидеть хорошие примеры такого подхода?

1 Ответ

0 голосов
/ 06 ноября 2018

Вам не нужно указывать :state для подклассов. Если вы этого не сделаете, он просто вызовет метод родителя.

(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)

И вызывающее пространство имен:

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

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

Это печатает:

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

...