我们在不同名称空间中的记录和协议存在一些问题。
我们在命名空间foo.proto中有一个协议。
(ns foo.proto)
(defprotocol Proto
(do-stuff [this x y]))
我在命名空间foo.record中有记录RecordA:
(ns foo.record
(:require [foo.proto :as proto]))
(defrecord RecordA [bar])
;; RecordA implements the protocol:
(extend-type RecordA
proto/Proto
(do-stuff [this x y] (* x y (:bar this))))
只要我们在repl中,这就可以正常工作。现在,如果我们在另一方面制作一个uberjar并运行我们得到的代码:
没有实现方法::协议的做法:#'foo.proto / Proto for class
另一方面,如果我们在类型声明中实现协议,如下所示:
(defrecord RecordA [bar]
proto/Proto
(do-stuff [this x y] (* x y (:bar this))))
我们不再收到错误(花了一些时间才弄明白)。 另外,如果我们将Proto的声明移动到与RecordA相同的ns中,我们也不会得到错误。
我的问题:
在声明中实现以及在extend-type或extend-protocol中有什么区别?
如果我们将记录和协议声明移到同一个ns中,为什么它会起作用?
由于
答案 0 :(得分:4)
问题可能在于如何将记录和协议包含在您正在使用它们的文件中。以下适用于我:
record.clj
(ns testclojure.record
(:require [testclojure.proto :as proto]))
(defrecord RecordA [bar])
(extend-type RecordA
proto/Proto
(do-stuff [this x y] (* x y (:bar this))))
proto.clj
(ns testclojure.proto)
(defprotocol Proto
(do-stuff [this x y]))
core.clj
(ns testclojure.core
(:require [testclojure.record :refer [->RecordA]]
[testclojure.proto :refer :all])
(:gen-class))
(defn -main [& args]
(-> (->RecordA 2)
(do-stuff 2 6)
(println)))
在lein uberjar
之后直接运行jar,我得到24的正确答案。
至于为什么它适用于命名空间和扩展的不同组合,defrecord
创建一个Java类,而extend-type
在协议的:impls
集合中创建一个条目。
答案 1 :(得分:1)
我遇到了同样的问题:
我正在尝试将协议扩展到我已经与协议实现分开定义的记录,即我正在使用(扩展协议)而不是定义与记录内联的实现。 Record,协议和实现都在同一名称空间中。但是,当我试图调用它时,它抱怨没有实现。
(extend-protocol MyProtocol
myns.message.Message
(my-protocol-function [this] (println "got here")))
(my-protocol-function new-msg)
=> IllegalArgumentException No implementation of method: :my-protocol-function of protocol: #'myns.connector/MyProtocol found for class: myns.message.Message clojure.core/-cache-protocol-fn (core_deftype.clj:544)
但是,如果我看看扩展器,我会在那里看到我的记录
(extenders MyProtocol)
=> (myns.message.Message)
但是(extends?)是假的
(extends? MyProtocol myns.message.Message)
=> false
如果我将协议定义内联到记录中,则所有内容都按预期工作。
答案 2 :(得分:-2)
您需要在(:import (foo.proto Proto))
命名空间声明中导入协议foo.record
。