This week's book giveaway is in the OO, Patterns, UML and Refactoring forum. We're giving away four copies of Refactoring for Software Design Smells: Managing Technical Debt and have Girish Suryanarayana, Ganesh Samarthyam & Tushar Sharma on-line! See this thread for details.
In Khalid Mughal book - came across the statements regarding equals method defined in a superclass and subclass: Please can someone explain the meaning of the below statements with suitable examples.....
A typical pitfall in broken transitivity is when the equals() method in a subclass calls the equals() method of its superclass as part of its equals comparasion. The equals() method in the subclass has ususally code equivalent to the following line.
The idea is to compare only the subclass-specific aspects in the subclass equals() method and to leverage on superclass equals() method..............
The problem lies in getting the equivalence contract fulfilled bilaterally between the superclass and the subclass equals() methods.
If the subclass equals method does not interoperate with superclass objects : symmetry is easily broken .If the subclass equals method does interoperate with superclass objects transitivity is easily broken.
If the superclass is abstract, leveraging on the superclass equals()method works well. .... The subclass equals() method can safely call the superclass equals() method to compare the superclass specific aspects of subclass objects.
Requirements: 1) The hashCode method must return the same integer value every time it is invoked on the same object during the entire execution of a Java application or applet. It need not return the same value for different runs of an application or applet. The Java 2 platform (Java 2) documentation further allows the hashCode value to change if the information used in the equals method changes.
2) If two objects are equal according to the equals method, they must return the same value from hashCode.
3) The equals method is reflexive, which means that an object is equal to itself: x.equals(x) should return true.
4) The equals method is symmetric: If x.equals(y) returns true, then y.equals(x) should return true also.
5) The equals method is transitive: If x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
6) The equals method is consistent. x.equals(y) should consistently return either true or false. The Java 2 javadoc clarifies that the result of x.equals(y) can change if the information used in the equals comparisons change.
7) Finally, x.equals(null) should return false.
Object provides a simple implementation of equals. It just tests the two objects for referential equality: does x equal y? Some of the standard Java classes override this to provide a more useful notion of equality -- usually content equality (i.e., is some or all of the data in the two objects identical?).
The equals implementation of java.lang.String, for example, returns true if the two objects are both String objects containing exactly the same characters in exactly the same order. The equals method of java.awt.Dimension returns true if the passed-in object is a dimension with the same width and height as the Dimension object executing the equals method.
The default implementation of hashCode provided by Object returns something corresponding to the object's address in memory or location in the Java virtual machine's global object array. Again, some of the standard Java classes override this method.
String, for example, overrides the hashCode implementation in Object to return a hash of some or all of the characters making up the String. This allows two String objects with the same characters in the same order to return the same hash value. Dimension uses the hashCode method provided by Object.
Now for the bad news: It's almost impossible to override equals and hashCodefor mutable classes and provide useful, correct and safe implementations for both methods.
To see why, consider the class java.awt.Dimension. This class overrides equals, but not hashCode. Dimension's JDK 1.1 implementation of equals looks like this:
This is a fairly reasonable implementation of content equality: if two Dimension objects have the same width and height they're equal, otherwise they aren't. So, what's wrong? The first problem is that because Dimension doesn't override hashCode, it's possible to have two Dimension objects that are equal, but return different hashCode values. This violates requirement (2) from above.
Second, testing the input parameter using instanceof Dimension creates problems of its own. Consider a child class: ThreeDeeDimension. Objects of type ThreeDeeDimension should test as equal only if they have identical height, width and depth. ThreeDeeDimension might look like this:
Unfortunately, this implementation of equals doesn't meet requirement (4) listed above. The following code snippet shows this:
I can fix this problem by rewriting the equals method of Dimension. If I write equals in Dimension like this, the ThreeDeeDimension class above meets requirement (4):
Now, objects of type ThreeDeeDimension won't return true when compared to objects of type Dimension. You still have a problem with both Dimension and ThreeDeeDimension because they don't meet requirement (2): objects that test as equal should have identical hashCode values. So, how is content-equality implemented in mutable classes? One example with both equals and hashCode is:
This implementation meets all the requirements, including requirement (6) with the clarification provided by Java 2. Immutable classes make implementing a useful and safe hashCode easier. In this case, you can use the data in the class to generate a hash value because that data will never change. In the example above, if "x" was guaranteed to never change, I could have implemented hashCode like this:
The key points to remember when implementing equals and hashCode:
These are not simple methods to implement. There are many details specified in each method's contract. You must implement these two methods together. You can rarely implement just one of them. It is difficult to implement a correct, useful, and safe hashCode method for mutable classes. Making classes immutable makes implementing the hashCode and equals methods much easier. Java 2 allows the value returned by hashCode to change if the underlying data changes, but you should be wary of doing this because data can then be stranded in hashtables. You must pay attention to inheritance, especially when implementing equals. This means comparing classes with getClass rather than with instanceof. Once a class has overridden equals and hashCode, the child classes may also require their own implementations. [ August 07, 2005: Message edited by: Sherry Jacob ]