Friday, June 3, 2011

Getting your equals right

I am still amazed when some of the programmers either don't use the equals properly or just don't use it at all. Here is some of the examples of when to use it and the rules of thumb according to Joshua Bloch from Effective Java (my favorite Java Book).




The class is very simple, the user passes a total amount of shares and the shares that are allocated to that total. Pretty straight forward. Here is the test case.

By following these rules, here is t


This does works. But we can do better. We can compared the objects and see if they are indeed correct. This can also helps us in future projects. As you can see, the class is immutable, which means that we can easily share this object without compromising the state. We can do this by overriding the "equals" method in the Share's class. But first, we need to remember the rule that the equals method implements the equivalence relation. It is:
  • Reflexive: an object must be equals to itself
  • Symmetric: any two objects must agree on whether they are equal
  • Transitive: if one object is equal to a second and the second object is equal to a third, then the first object must be equal to the third.
  • Consistency: if two objects are equal, they must remain equal for all the time, unless one (or both) of them is modified
  • Non-nullity: all objects must be unequal to the invocation of o.equals(null)
Here is the class with the equals override:





Now we can compared the actual objects:


Much better, but according to Item 9 in Effective Java, we should always override hashCode when we override equals. Failure to do so will result in a violation of the general contract of Object.hashCode which will prevent your class from functioning properly in conjunction with all hash-based collections, including HashMap, HashSet, and HashTable. The good thing is that Eclipse can be a great tool. By just clicking on Source code > Generate hashcode and equals you get this:

2 comments:

  1. Nice. +1 for making a mention of Eclipse's utilities for generating the 'hashCode' method. I also like that it is capable of generating 'equals'.

    The one thing I'd like to see is auto-generation of 'toString'. Since it doesn't have it, I typically either use the ToString utility classes found in Apache Commons Lang package (or manually create them using the same Commons Lang rules.)

    ReplyDelete
  2. One comment, though. Unless I have to interface with code that requires them, I don't like using primitive wrappers (as parameters or fields.)

    I prefer to use raw types (int, long, etc.) The moment you use these wrappers, you have to deal and handle with NPEs.

    I don't like the auto-boxing feature (Java 5 and above). I've gotten bitten by 'junior' code that passes a wrapper instance, say an Integer, to a method taking an int without checking if the instance being passed is null or not.

    With the pre-5 JDKs, that would not compile and the programmer would have to deal with null cases (and either raise a NPE or use a default.)

    But in the post JDK-4 world, that same code can compile without a glitch, because it gladly churns the auto-boxing instructions. And since Java has no concept of nullable and non-nullable types, it cannot require the programmer to check for null cases for the stuff it happily auto-boxes.

    In the pre-JDK 5 world, I would have had no problems using primitive type wrappers. But now, with auto-boxing and without semantic support for mandatory null-checks, it's just pain.

    ReplyDelete