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.
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) == truethen
y.equals(x) == true
- Transitive - if
x.equals(y) == trueand
y.equals(z) == truethen
x.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
Timestampclass which extends
Dateand 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.URLrelies 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
instance ofto check for correct type
- cast to correct type
- compare significant fields
- check if the argument
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.
toString() (item 10)
… because it makes debugging much easier.
Be cautions of
clone (item 11)
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
- Generally, and especially when extending a class, when overriding
clone, you should return the object returned by
super.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.
clonedoes not copy mutable object fields, so
super.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.
clonecreates an object without using the constructor, it must ensure that all the invariants are correct after creation.
clonemust 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.
Comparable (item 12)
Comparable deals with order comparison, and is required when using for example
- Comparable has a similar contract as
equals, which may lead to erratic behavior may when broken. The contract requires symmetry, reflexivity and transitivity.
equalswhich is inconsistent with
compareTocan create duplicates in some collections.
- Float and Double have their own static
compareTomethods that should ease handling floating point issues.
- Take care when subtracting integers to create return value of
compareTobecause it can create overflow (i.e. outside of
Integer.MAX_VALUE) and create wrong return value! If
iis large positive value and
jis large negative value then
i-jwill overflow and return a negative value.