【问题标题】:how to extend java class with protocol having a method the same name than a clojure.core function?如何使用具有与 clojure.core 函数同名的方法的协议扩展 java 类?
【发布时间】:2014-06-08 18:47:24
【问题描述】:

我正在尝试使用此协议扩展一个简单的 java 类 toxi.color.ColorList

(defprotocol countable
  (count [this]))

(extend-protocol countable
  ColorList
  (count [this]
    (.size this)))

当我评估这段代码时,我看到了这些警告

Warning: protocol #'dat00.protocols/countable is overwriting function count

WARNING: count already refers to: #'clojure.core/count in namespace: dat00.protocols, being replaced by: #'dat00.protocols/count

但这很好用:

(count (ColorList.))
=> 0

但是如果我在同一个文件(或命名空间)中尝试这个

(count (range 5))
=> IllegalArgumentException No implementation of method: :count of protocol: #'dat00.protocols/countable found for class: clojure.lang.LazySeq  clojure.core/-cache-protocol-fn (core_deftype.clj:541)

所以我的问题是:
我是否误解了有关协议的一些细节?

谢谢!

【问题讨论】:

  • 协议方法导致在当前命名空间中定义同名函数。当您从某些 Clojure 代码调用协议方法时,您实际调用的是一个生成的函数,该函数查找并分派到适当的实现。
  • 在此先感谢 Alex,但是您将如何解决这种情况“当前命名空间冲突”?
  • 与任何其他命名空间冲突相同,如下面的答案所述。它恰好是导致冲突的协议方法这一事实并不重要。
  • 感谢@Alex 我已经在下面发布了针对这种情况的 2 个工作解决方案,具有多方法和扩展协议,都更改了函数名称

标签: clojure


【解决方案1】:

你有一个命名空间冲突。

定义协议时,您是在当前命名空间中定义调度函数。如果您真的想使用“count”,则必须在命名空间声明中排除 clojure.core 版本。

(ns so.protocols 
  (:refer-clojure :exclude [count]))

现在在该命名空间中,您可以使用“count”方法定义您的协议。如果您随后想要该命名空间中的核心版本 count,您可以在命名空间中为其添加前缀 clojure.core/count

你的协议的用户会想要给你的命名空间起别名。例如,

(ns user 
  (:require [so.protocols :as p]))

所以p/count 是您的协议方法,count 是核心。

【讨论】:

  • 感谢您解释解决此命名空间重叠的详细信息!但是我的问题(没有很好解释)也和表达式问题stackoverflow.com/questions/3596366/…有关,然后我想在相同的情况下使用相同的name-method-interfaz。
【解决方案2】:

在您的协议countable 中定义了函数clojure.core/count 和方法count。就像警告说的那样:您通过创建一个具有名为count 的方法的接口,将名为count 的别名覆盖为clojure.core/count

(range 5) 返回的LazySeq 对象没有实现您的countable 协议。您仍然可以使用(clojure.core/count (range 5)) 来计算它。

您可能想要做的是实现clojure.lang.Counted 接口而不是您自己的接口。

【讨论】:

  • "你可能想做的是实现 clojure.lang.Counted 接口而不是你自己的。" - 不幸的是,OP 希望将协议扩展到现有的 Java 类 - 没有迹象表明可以修改类。
  • 然后他应该通过 defrecord 或使用 reify 创建一个 Clojure 包装器。
  • 抱歉@lgrapenthin 我没有选中您的解决方案,因为我无法修改 Java 类,并且 defrecord 或 reify 也不起作用,因为有时我没有创建实例 :(
  • 您有两个选择: 1. 创建一个功能齐全的 ToxicLib Clojure 包装器(至少是您直接或间接使用的功能)。这是很多工作,只有在有显着好处的情况下才应该这样做。 2. 这是我的建议:直接使用 Java 互操作,这是 Clojure 中的一个常见习语 - ColorList 甚至不是一成不变的,那么为什么要让它适应 Clojure 呢?只需使用 .size - 其他所有内容都会让您的代码的读者感到困惑(我认为 - 它是 Clojure coll 吗?)。
【解决方案3】:

谢谢大家,

我在这里发布了我为这个案例找到的 2 个可行的解决方案,感谢大家的 cmets

在所有情况下,我都必须更改函数的名称

似乎多方法(我还不确定性能后果...)也可以解决这里的表达式问题

(defmulti count type)

(defmethod count toxi.color.ColorList [a]
  (.size a))
(defmethod  count clojure.lang.LazySeq [a]
  (count a))

(defprotocol countable
  (get-count [this]))

(extend-protocol countable
  ColorList
  (get-count [this]
    (.size this))
  clojure.lang.LazySeq
    (get-count [this]
    (.count this))
  )

(get-count (ColorList.)) => 0
(get-count (range 5)) => 5

这对我有用,虽然我想做的(并且通过 ns 碰撞,我意识到我的概念错误)是在同一个 ns 中使用不同接口的“同名方法”:) ...假设受clojure/java interop sintaxis的影响(参数定义了fn而不是相反)——

【讨论】:

    猜你喜欢
    • 2015-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    相关资源
    最近更新 更多