Effective Java - Methods Common to All Objects
Methods Common to All Objects (Ch 3)
This is a short summary of Joshua Blochs book Effective Java chapter 3. I have only included items that I find relevant.
The general equals()
contract (item 8)
The equals contract describes the equivalence relation as:
x.equals(null) == false
- Reflexive -
x.equals(x) == true
- Symmetric - if
x.equals(y) == true
theny.equals(x) == true
- Transitive - if
x.equals(y) == true
andy.equals(z) == true
thenx.equals(z) == true
- Consistent - multiple invocation of equals on the same, unmodified objects, return the same value.
There are some notable properties to be vary of:
- If the above contract is violated, behaviour of other objects (like
List.contains()
) is undefined. - There is no way to extend an instantiable class with a new value field/component while preserving the equals relation, unless you are willing to forgo the benefits of OO abstractions.
- Note that in case the base class is abstract, it is fine.
- An example where this is a problem is Java
Timestamp
class which extendsDate
and violates the symmetry part. If both are intermixed in a Collection, they can create erratic behavior. - The solution is prefer composition over inheritance.
java.net.URL
relies on IP address of the hosts associated with the URL which require network access, and therefore breaks consistency.- The book gives recipe for creating an optimal equals:
- check if the argument
==this
- use
instance of
to check for correct type - cast to correct type
- compare significant fields
- check if the argument
Always override hashCode()
when you override equals()
(item 9)
The hashcode is used by hash based structures. The most important part of the hashcode contract states that equal objects must return equal hashcodes. In addition, the hashcode function should return different values for unequal objects for performance. Without a correct hash code implementation, hash based structures will perform poorly and even worse, regard equal objects as unequal. If a constant value is provided as hashCode e.g. ... return 42
, then hash tables degenerate to linked lists and program supposed to run in linear time run on quadratic time.
Always override toString()
(item 10)
… because it makes debugging much easier.
Be cautions of clone
(item 11)
Implementing Clonable
makes Object.clone()
return a field-by-field copy, otherwise it throws CloneNotSupportedException
. Usually, cloning creates an object, but bypasses the constructor. There are several challenges of implementing clone
:
- Generally, and especially when extending a class, when overriding
clone
, you should return the object returned bysuper.clone()
to get the right type. This is not enforced and it is up to the user to do this, but without it, the clone can break. clone
does not copy mutable object fields, sosuper.clone()
will refer to same object fields. Fields must be manually cloned.- this essentially means that fields cannot be `final when used with clone, unless the same field value can be shared.
- Since
clone
creates an object without using the constructor, it must ensure that all the invariants are correct after creation. clone
must be called recursively on internal lists/arrays.
The general advise is to avoid using and implementing Object.clone()
and rather use copy constructors public Yum(Yum yum)
or factories, except when copying arrays.
Implementing Comparable
(item 12)
Comparable deals with order comparison, and is required when using for example TreeSet
, TreeMap
, search
or sort
.
- Comparable has a similar contract as
equals
, which may lead to erratic behavior may when broken. The contract requires symmetry, reflexivity and transitivity. equals
which is inconsistent withcompareTo
can create duplicates in some collections.- Float and Double have their own static
compareTo
methods that should ease handling floating point issues. - Take care when subtracting integers to create return value of
compareTo
because it can create overflow (i.e. outside ofInteger.MAX_VALUE
) and create wrong return value! Ifi
is large positive value andj
is large negative value theni-j
will overflow and return a negative value.