8. Obey the general contract when overriding
equals() should implement equivalence relation:
- Reflexive: for any
- Symmetric: for any
x, ynot null, if
- Transitive: for any
x, y, znot null, if
- Consistent: for any
xnot null, multiple invocations of
trueor consistently return
false, provided no information used in
equalscomparisons on th objects is modified
- for any
x.equals(null)must return false
There is a fundamental problem of equivalence relations in OOP: there is no way to extend an instantiable class and add a value component while preserving the
To ensure Reflexivity
Difficult to violate this rule.
To ensure Symmetry
equals(), check type first, then do logic comparison.
To ensure Transitivity
ColorPoint is a subclass of
Point, we must override the
equals() inherited from
ColorPoint may be:
This code violates symmetry, since a
Point p with attributes as
p.equals(cp) will return
There is no perfect way to solve the problem. Composition is better than inheritance. A good way is:
9. Always override
hashCode when you override
Two distinct instances may be logically equal according to a class's
equals method, but to Object's
hashCode method, they're just two objects with nothing much in common. Therefore Object's
hashCode method returns two seemingly random numbers instead of two equals numbers as required by the contract.
An example here:
We try to get the the same phone number from the map, but the output is
Here we have two objects. One is inserted into the Map. Since we haven't override the
hashCode() method, these two objects has different hash code, thus
put method put the first object into a hash bucket, but
get method is looking for the object in the other hash bucket.
If you are not familiar with hash bucket, here is my explanation. In hashing algorithms, hash table can be thought of a list of list,
List<List> lst. Each element in
lsthas a unique hash code in
lst, representing a list(this is the hash bucket we were talking about).
You can have a
hashCode() method, returns a good hash value, NOT something like
A good hash function tends to produce unequal hash codes for unequal objects. This is exactly what is meant by the third provision of the
hashCode contract. Ideally, a hash function should distribute any reasonable collection of unequal instances uniformly across all possible hash values.
hashCode() method, with lazy instantiation
Don't be tempted to exclude significant parts of an object from the hash code computation to improve performance.
10. Always Override
Cloneable interface was intended as a
mixin interface for objects to advertise that they permit cloning.
clone method of
Object is protected. You cannot, without reflection, invoke the
clone method on an object merely because it implements
Cloneable. Even a reflective invocation may fail, as there is no guarantee that the object has an accessible
Cloneable interface has no methods, but determines the behavior of Object's protected clone implementation: if a class implements
clone method returns a field-by-field copy of the object; otherwise it throws
A copy of an object means:
- x.clone() != x
- x.clone().getClass() == x.getClass()
clone method functions as another constructor. You must ensure that it does no harm to the original object and that it properly establishes invariants on the clone.
For something like a stack, where there are already many elements, a
clone method should like this:
elements field is
final, this may not work.
clone() method of a HashTable class
For long linked list, stack is easy to overflow. In
deepCopy(), we can replace recursion with iteration.
To sum up, better not extends
Cloneable. Copy Constructor is more appropriate and flexible.
12. Comparable interface
equals(), there are some rules that
compareTo() must follow. In the following description, the notation
sgn(expression) designates the sign function in math.
- The implementor must ensure
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))for all
- The implementor must ensure that the relation is transitive:
x.compareTo(y) > 0 && y.compareTo(z) > 0implies
x.compareTo(z) > 0
- The implementor must ensure that x.compareTo(y) == 0 implies that
sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z
- Optional requirement:
(x.compareTo(y) == 0 ) == (x.equals(y))