Whenever you override the equals() and hashCode() functions of a class, it is very important that you do so correctly. Bugs in these functions are often quite insidious, because they will not fail catastrophically. Your application will compile and probably do something reasonable, passing any smoke test you throw at it. However, the output will be subtly incorrect. Moreover, by the time you realize that some has gone wrong, there is little information about what caused the error. This is because the error occurred 10 steps ago, nested five levels deep, when a member of a HashSet went MIA. While a NullPointerException is normally trivial to diagnose and fix, you can easily lose several hours tracking down an equals()/hashCode() bug.
There are many excellent tutorials out there on equals() and hashCode(). The best source is probably Items 7 and 8 of Effective Java by Josh Bloch, and we have a couple copies of this book floating around the office. A good online tutorial is Java theory and practice: Hashing it out, by another Java guru, Brian Goetz.
To test your understanding, make sure that you can answer the following questions. I often ask variants of the first two during interviews. The third comes from an error we’ve seen more than once in our code base.
- Suppose we have a Media class, composed of title and contents fields. What can go wrong if equals() is based on both fields, while hashCode() is based solely on title?
- Conversely, what can go wrong if hashCode() is based on both fields, but equals() is based solely on title?
- Consider a class A which overrides equals() and hashCode(). If you create a class B which extends class A, and use Eclipse to generate your equals() and hashCode() functions for class B, then the output of class B’s hashCode() function will depend on the value of super.hashCode(). What happens if you refactor class B so that it no longer extends class A?