Clojure:是否可以继承用 :gen-class 定义的 class 的状态?
Clojure: is it possible to inherit state of class defined with :gen-class?
我正在尝试研究一些以 Clojure 作为工作语言的 Java 库。该库(与 Java 中的往常一样)非常 object-oriented 并且在客户端代码中需要 class 层次结构。我定义了一个从库 class 继承的 class 以及一些额外的方法,数据作为可变字典存储在 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)
然后我创建了一个 child class 继承自我的 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
。
快速而肮脏的修复是在 child class 中重新定义 -init
函数,如下所示:
(defn -init []
[[] (ref {:library-object-one (LibraryObjectOne.)
:library-object-two (LibraryObjectTwo.)})])
(即从 parent class 复制初始化代码)。但这是对 DRY 原则的严重违反。有没有办法从 parent class?
继承状态
我知道它根本不是惯用的 Clojure,而是对 :gen-class
API 的一种滥用,它仅用于互操作性目的。也许我不应该在我这边使用继承,我必须以某种 non-OOP 方式实现多态性(例如,通过修改存储在 state
字典中的函数和值)。如果是这样,我在哪里可以看到这种方法的好例子?
您不必为子类提供 :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}
我正在尝试研究一些以 Clojure 作为工作语言的 Java 库。该库(与 Java 中的往常一样)非常 object-oriented 并且在客户端代码中需要 class 层次结构。我定义了一个从库 class 继承的 class 以及一些额外的方法,数据作为可变字典存储在 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)
然后我创建了一个 child class 继承自我的 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
。
快速而肮脏的修复是在 child class 中重新定义 -init
函数,如下所示:
(defn -init []
[[] (ref {:library-object-one (LibraryObjectOne.)
:library-object-two (LibraryObjectTwo.)})])
(即从 parent class 复制初始化代码)。但这是对 DRY 原则的严重违反。有没有办法从 parent class?
继承状态我知道它根本不是惯用的 Clojure,而是对 :gen-class
API 的一种滥用,它仅用于互操作性目的。也许我不应该在我这边使用继承,我必须以某种 non-OOP 方式实现多态性(例如,通过修改存储在 state
字典中的函数和值)。如果是这样,我在哪里可以看到这种方法的好例子?
您不必为子类提供 :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}