在Clojure的deftype中覆盖equals,hashCode和toString方法

11

我正在尝试使用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)))))

然而,equals方法仍需要一些方法来确定b参数是否实现了Location协议。

什么是正确的方法?我走在正确的轨道上吗?

1个回答

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

在Clojure中,defrecord定义的内容有可能会在未来具有自定义的读取语法,以便能够可读性地打印和使用Clojure读取器进行读取。除非您真的喜欢您的toString版本,否则建议您也应该考虑这一点。目前,记录已经可以以人类可读的形式打印出来,但不一定适合机器读取。

user> (Point. 1 2)
#:user.Point{:x 1, :y 2}

我的实现看起来除此之外还不错吗? - mikera
不确定我是否有资格批评它,但我稍微扩展了我的回答。 - Brian Carper
太酷了,谢谢Brian!同意defrecord看起来在这种情况下通常会非常好用,我想我只是为了尽可能深入地理解Clojure中“接近底层”的编码而玩弄deftype。 - mikera
3
如果你还没有阅读http://clojure.org/datatypes中的"Why have both deftype and defrecord?"部分,请先阅读。deftype似乎用于实现Clojure本身的一些部分,或者实现自己的新的抽象数据类型。而defrecord似乎用于“数据”,即您的Points或其他旨在高级别上使用的数据。如果您可以轻松地将您的类型替换为哈希图(在您的情况下我认为是可能的),那么defrecord可能是您想要的。这只是我的理解。 - Brian Carper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接