我有一个协议和几个deftypes在一个工作区内实现它.如何列出实现以下协议的所有deftypes?
我已经找到了从(ns-public)过滤数据的解决方案,但我不喜欢它,因为它使用了一些"魔法"来完成工作,因为我没有找到正确的方法来实现我的目标与满足?并延伸?.
有任何想法吗?
(defprotocol Protocol
(foo[this] "just an interface method"))
(deftype Dummy [] Protocol
(foo[this] "bar"))
(defn implements? [protocol atype] "fn from clojure sources"
(and atype (.isAssignableFrom ^Class (:on-interface protocol) atype)))
(defn list-types-implementing[protocol]
(filter (fn[x] (let [[a b] x]
(when (.startsWith (str a) "->") ; dark magic
(implements? protocol
(resolve (symbol
(.replace (str a) "->" "")))))
))
(ns-publics *ns*)))
(list-types-implementing Protocol) ; => ([->Dummy #'user/->Dummy])
(let [[a b] (first(list-types-implementing Protocol))]
(foo (b)) ; => "bar"
)
Run Code Online (Sandbox Code Playgroud)
一般来说,这将是一个需要解决的棘手问题,因为类型可以通过两种不同的方式来满足协议。extend-type您可以使用和函数将任何现有 Java 类扩展为协议extend-protocol(这是一个非常强大的功能,因为它允许您扩展代码以使用内置 Java 或 Clojure 类型,或其他您不需要的第三方类型)控制)。或者,您可以直接指定协议实现作为deftype或中类型定义的一部分defrecord。这两种机制的实现方式不同。
第一种情况(通过extend-type或扩展extend-protocol)会更容易解决,因为被扩展的类型将附加到协议本身(协议本质上相当于生成的 Java 接口加上带有有关协议元数据的 Clojure 映射)。:impls您可以通过查看协议映射中的键来找到扩展协议的类型:
user=> (defprotocol MyProtocol (foo [this] "Protocol function"))
user=> (deftype MyType [])
user=> (extend-type MyType MyProtocol (foo [_] "hello foo!"))
user=> (keys (:impls MyProtocol))
(user.MyType)
Run Code Online (Sandbox Code Playgroud)
deftype第二种情况(通过或直接实现协议defrecord)更加困难,因为为类型或记录生成的 Java 类将直接实现协议定义的 Java 接口(您可以在 或 中实现任何 Java 接口deftype,defrecord而不仅仅是协议)。任何查找以这种方式扩展协议的类型的方法都需要进行一些扫描和反射。本质上,您要问的是,“如何找到实现给定接口的所有 Java 类?”
在典型的 Java 应用程序中,您可能会执行类似扫描类路径目录中的 .class 文件并对其进行内省的操作,但这在 Clojure 中不起作用,因为很可能没有适合您的类型的 .class 文件(字节码是动态生成的)。如果您对自己的实现不满意,您可以查看此问题的答案,看看它是否更符合您的喜好: