Clojure 协议是否可以将新状态引入现有类?

Clojure 协议是否可以将新状态引入现有类?,clojure,Clojure,我了解如何使用协议向现有类引入新行为,但它们(或任何其他Clojure机制)是否可能向现有类引入状态?更具体地说,我希望能够将映射与来自第三方库的类的实例相关联。您可以使用设置状态和获取状态函数创建协议。然后使用围绕某种hashmap构建的实现,将它们扩展到所需的类。您不能将状态存储在外部对象中,但可以让您的函数共享由对象键控的哈希表的ref。我认为这个解决方案可能有很多问题,比如如何检测对象何时将被GCed,以及其状态是否需要清除?您可以使用WeakReference或其他东西,但它并不琐碎。

我了解如何使用协议向现有类引入新行为,但它们(或任何其他Clojure机制)是否可能向现有类引入状态?更具体地说,我希望能够将映射与来自第三方库的类的实例相关联。

您可以使用
设置状态
获取状态
函数创建协议。然后使用围绕某种hashmap构建的实现,将它们扩展到所需的类。您不能将状态存储在外部对象中,但可以让您的函数共享由对象键控的哈希表的ref。我认为这个解决方案可能有很多问题,比如如何检测对象何时将被GCed,以及其状态是否需要清除?您可以使用WeakReference或其他东西,但它并不琐碎。

协议在Java接口中的概念相似,因为它们根本不关心状态或表示,因此我非常确定,除非将状态存储在对象本身之外,否则您无法这样做。但是,您可以使用Clojure中扩展(子类化)类的各种其他方式来实现这一点,例如使用
代理
或使用
gen class
。(见
)

首先,协议是接口定义,通常不希望在接口中指定状态。您通常希望在接口的实现中设置状态,请参见下文

对于大多数允许您实现接口或扩展类的clojure构造,最明智的方法是使用闭包来捕获状态,尤其是在您不拥有类的情况下。您可以捕获可变类型来实现可变状态(尽管如果可以的话,这可能是您想要避免的)

一个不可改变的例子

注意,如果您想要可变状态,这里的f可以是ref、var或atom,而不是字符串

编辑:正如在评论中所注意到的,我可能没有完全清楚地说明这一点:您可以使用defprotocol定义基于函数的接口,然后使用reify创建该协议的实例以捕获状态


编辑2:很抱歉让人困惑。此代码实际上不适用于现有类,因为reify不支持它。代理可能是一种替代方法,尽管文档中没有明确说明协议中的函数名映射到接口方法的状态。

您可以存储状态。您只需要在get和set方法中使用一个atom或ref并引用和取消引用该atom或ref

该示例仅实例化一个对象;没有任何其他对象实例。具体化不是一个类定义(至少在概念上不是)。OP可以将其捆绑在一个函数中,以便在调用时绑定f,以便具体化到所需的任何内容。是的,对不起,我刚刚意识到(仅在删除后看到您的注释)-仍然对OP没有帮助,因为您不能具体化类,只能具体化接口或协议。但defprotocol只定义了一个接口。我将在我的帖子中更清楚地说明这一点。问题是“……它们(或任何其他Clojure机制)是否有可能将状态引入现有类”,并且您可以使用具体化和协议。协议文档甚至有一个明确的例子,使用协议的具体化。这是可能的,但我不推荐这样做。正如您所注意到的,这是一个非常麻烦的问题,代理或具体化已经允许您使用闭包捕获状态,这应该可以消除GC问题(通常工作起来更像您期望的lisp)。我同意这不是一个很好的解决方案,但如果所讨论的类被标记为
final
,则可能是必要的,在这种情况下,
proxy
不会有帮助(我认为
gen-class
无法克服这种描述)。这将是全局状态,而不是每个实例状态。看起来我必须使用
gen-class
gen-interface
的组合。接口将定义获取状态的函数。我希望以一种更动态的方式来实现这一点,以便将状态引入到相对大量的现有类中,但是
reify
不适用于具体的类。也许使用
gen接口
然后按需生成代理就可以了?您可以用gen类添加状态,用协议处理其余的状态。不确定这是否会使事情变得简单,但我认为最佳解决方案将在很大程度上取决于您的具体情况。
 (str (let [f "foo"] 
      (reify Object 
        (toString [this] f))))