【发布时间】:2019-04-09 19:07:09
【问题描述】:
我正在尝试研究一些使用 Clojure 作为工作语言的 Java 库。该库(与 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 {})])
…
但是当我在 -setSomeData 方法中为 ChildClass 实例调用 (get-field this :library-object-one) 宏时出现空指针异常 — :state 定义的字段没有被继承,并且没有键 :library-object-one 在字典。
快速而肮脏的解决方法是在子类中重新定义 -init 函数,如下所示:
(defn -init []
[[] (ref {:library-object-one (LibraryObjectOne.)
:library-object-two (LibraryObjectTwo.)})])
(即从父类复制初始化代码)。但这严重违反了 DRY 原则。有没有办法从父类继承状态?
我知道这根本不是惯用的 Clojure,而是一种对 :gen-class API 的滥用,该 API 仅用于互操作性目的。也许我不应该使用继承,我必须以某种非 OOP 方式实现多态性(例如,通过修改存储在 state 字典中的函数和值)。如果是真的,我在哪里可以看到这种方法的好例子?
【问题讨论】:
-
嗯,可以通过从子类的
-init函数返回[[] (nth (my-project.my-parent-class/-init) 1)]来完成类似于调用父类构造函数的操作。但它对我来说仍然像是一个杂物。
标签: java oop clojure functional-programming clojure-java-interop