Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding equals, hashCode and toString in a Clojure deftype

I'm trying to create a new type in Clojure using deftype to implement a two dimensional (x,y) coordinate, which implements a "Location" protocol.

I'd also like to have this implement the standard Java equals, hashCode and toString methods.

My initial attempt is:

 (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)))))

However the equals method still needs some way of working out if the b parameter implements the Location protocol.

What is the right approach? Am I on the right track?

like image 725
mikera Avatar asked Jun 10 '10 20:06

mikera


People also ask

Is it necessary to override both hashCode and equals method?

You must override hashCode() in every class that overrides equals(). Failure to do so will result in a violation of the general contract for Object. hashCode(), which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and Hashtable.

Why to override equals object and hashCode() method?

Case 1: Overriding both equals(Object) and hashCode() method Whenever it(hashcode) is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified.

When writing a Java class it is advisable to at least override equals hashCode and toString methods for that class?

If your model class will be used as a key of a Map or a Tree, or in a Set, its best practice to override equals and hashCode method (as it will be needed for comparing 2 model class objects). toString you will need to override, if you want you want to display your model class data in a efficient way.


1 Answers

To test if something satisfies a protocol, there's satisfies?.

Edit:

Protocols and datatypes are too new in Clojure (and still evolving fast) for me to remark much about what's idiomatic or not. But you should note that defrecord already implements type-and-value-based equality. Unless you really need a custom hashcode for your objects, you could consider using 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

You also get the added bonus of being able to access your fields via keyword lookup, and being able to put metadata on your objects, which defrecord gives you for free.

user> (:x (Point. 1 2))
1

It's possible that defrecord-defined things will have custom reader syntax someday in Clojure, so they can be printed readably and read back in with the Clojure reader. Unless you're really attached to your version of toString, you might keep this in mind as well. Right now, records already print human-readably if not machine-readably.

user> (Point. 1 2)
#:user.Point{:x 1, :y 2}
like image 61
Brian Carper Avatar answered Sep 28 '22 04:09

Brian Carper