我正在尝试使用deftype在Clojure中创建一个新类型来实现一个二维(x,y)坐标,它实现了一个“位置”协议。
我也想让它实现标准的Java equals,hashCode和toString方法。
我最初的尝试是:
(defprotocol Location
(get-x [p])
(get-y [p])
(add [p q]))
(deftype Point [#^Integer x #^Integer y]
Location
(get-x [p] x)
(get-y [p] y)
(add [p q]
(let [x2 (get-x q)
y2 (get-y q)]
(Point. (+ x x2) (+ y y2))))
Object
(toString [self] (str "(" x "," y ")"))
(hashCode [self] (unchecked-add x (Integer/rotateRight y 16)))
(equals [self b]
(and
(XXXinstanceofXXX Location b)
(= x (get-x b))
(= y (get-y b)))))
但是,如果b参数实现了Location协议,则equals方法仍需要一些方法。
什么是正确的方法?我是在正确的轨道上吗?
答案 0 :(得分:7)
为了测试某些内容是否符合协议,有satisfies?
。
协议和数据类型在Clojure中过于新颖(并且仍然在快速发展),因此我可以很多地评论什么是惯用语。但是您应该注意defrecord
已经实现了基于类型和值的相等性。除非您确实需要对象的自定义哈希码,否则可以考虑使用defrecord
。
(defrecord Point [#^Integer x #^Integer y]
Location
(get-x [p] x)
(get-y [p] y)
(add [p q]
(let [x2 (get-x q)
y2 (get-y q)]
(Point. (+ x x2) (+ y y2)))))
user> (= (Point. 1 2) {:x 1 :y 2})
false
user> (= (Point. 1 2) (Point. 1 2))
true
您还可以通过关键字查找访问您的字段,并能够在defrecord
免费提供的对象上放置元数据。
user> (:x (Point. 1 2))
1
defrecord
定义的东西有可能在Clojure中有自定义的阅读器语法,因此它们可以被可读地打印并用Clojure阅读器读回。除非你真的依附于你的toString
版本,否则你也可以记住这一点。现在,如果不是机器可读的话,记录已经是人类可读的。
user> (Point. 1 2)
#:user.Point{:x 1, :y 2}