Friday, April 15, 2011

Methods common to all objects - Part 1


All the non-final methods of the Object class have explicit general contracts and any overrides of these methods should adhere to those contracts failure of which might result in abnormal behaviour.

Obey the general contracts when overriding equals()
  1. In general, an object is always one and only equal to itself as per the default implementation.
  2. Equals are overriden when there is a logical equality between 2 instances. This is more appropriate in case of Value classes.
  3. Instance controlled classes might reuse the equivalent objects efficiently by not allowing 2 objects with equivalent values to get created. In such cases equals() need not be overriden.
  4. The equals method should be reflexive, transitive, symmetric, consistent, and if x is not null then x.equals(null) should always be false.
  5. There is no way to extend an instantiable class and add a value component while preserving the equals contract. The workaround is to use composition rather than inheritence and then add the value component. Remember that such problem wont exist in case of abstract parent classes as you cant create instances of them leading to such different equals() method being called.
  6. Using getClass() method in the equals and comparing only when the current object's class is equal to the other object violates Liskov substitution principle.
Liskov substitution principle: Any important property of a type should also hold for its subtypes, so that any method written for the type should work equally well for its subtype.
It is very easy to break symmetry and transitivity. This is because when you compare a super class object with a subclass object, the subclass might have overriden the implementation of equals method. It might lead to breakage of symmetry behaviour.
SuperClassObject.equals(subClassObject);
A good template of equals() is as follows:
boolean equals(Object o) {
    if (o == this) return true;
    if (!(o instanceof Klass)) return false;
    Klass obj = (Klass) o;
    return obj.a == this.a;
}
While comparing, for non-float fields use ==, for references use equals(), and for float and double use Float.compare and Double.compare.

Always override hashCode when you override equals
As per the Object contract for hashCode, hashCode should always return the same code provided none of the fields used in the equals() comparison has changed. Two objects which return true for equals() should always have the same hashCode. If two objects are not equal, then as far as possible they should not have the same hashCode which might result in increased performance of the hash tables.
  1. You should ALWAYS exclude fields that are not used in equals() while computing the hashCode.
  2. If computing the hashcode is costly for a immutable object, you can consider caching the hashCode for furture references.
Always override toString
As per the Object contract, toString is expected to provide a concise but informative representation that is easy for a person to read. Also it is recommended that all classes override this method.
  1. toString method should return details on all interesting information contained in the object.
  2. Whether or not you decide to specify the format of the toString, you should clearly document your intentions. The disadvantage of specifying the format is that, client may write code which is tightly bound to the format and so changing the format at the later stage becomes difficult.
  3. Also note that, provide accessors to ALL the parameters that you show in the toString. This will help the clients not to rely on just the toString method to get the detail.
I have taken these points from the book "Effective Java" which I consider as a MUST READ book for every JAVA developer.

No comments:

Post a Comment