Sundarrajk's Weblog

Archive for the ‘Effective Java’ Category

This is the second in the series of posts based on the book Effective Java by Joshua Bloch. The first post was about creating and destroying objects efficiently and effectively.
In this post we will see how to write correctly some of the the standard methods that can be implemented by every object.

Obey the general contract when overriding equals

The easiest option is not to implement equals method if it is not required. Under the following conditions do not implement the equalsmethod
1.       Each instance of the class is inherently unique
2.       You don’t create whether the class provides a “logically equality” test
3.       A super class has already overridden equals, and the behavior inherited is appropriate.
4.       The class is private or package-private and you are certain that its equals method will never be invoked.

Rules to adhere to

Override equals method if and only if you know that equals will be invoked implicitly or explicitly on the class and the default implementation will not be sufficient. Note that equals can be implicitly invoked if you look for List.contains or Map.containsKey or Map.containsValue or use it as a key in a Map or added to a Set.
If equals method is implemented it should satisfy the following conditions:
1.                   It should be reflexive: x.equals(x)must be true.
2.                   It should be symmetric: If x.equals(y)is true then y.equals(x) should also be true and vice-versa
3.                   It should be transitive: If x.equals(y)and y.equals(z)are true then x.equals(z) must also be true.
4.                   It is consistent: Multiple invocations of x.equals(y) should return the same result as long no information used in equals is modified.
5.                   For any non-null x, x.equals(null)should return false

Recipe for writing equalsmethod

1.       Use the == operator to check if argument is a reference to this object and if so return true.
2.       Use the instanceof operator to check if argument is an instance of the current class and if not return false. Note that this also handles the situation where the argument is null as a null object when checked against instanceof any other object returns a false.
3.       Cast the argument to the correct type.
4.       For each “significant” field in the class, check to see if that field of the argument matches the corresponding field of this object. If all such fields match then return true else return false.
a.       To compare any primitive value other than float and double use the == operator
b.      To compare float use float.floatToIntBits on both the values and use the == operator
c.       To compare double use double.doublToLongBits on both the values and use the == operator
d.      To compare any object use the equals method. Take care to handle null objects. Use this technique to compare object (field == o.field) || (field != null && field.equals(o.field))
5.       Override hashCode method if equals method is overridden.
6.       Do not substitute another type for Object in the equals declaration.
The signature of the equals method is
public boolean equals(Object o) {
}
Not
public boolean equals(MyClass o) {
}
7.       For performance structure the method such that the attributes that are most likely to change are compared before the attributes that are unlikely to change.
Always override hashCodewhen you override equals

One must override hashCode method in every class that overrides equals. If this is not adhered to then the class will not function as per expectations when used in any hash-based collections including HashMap, HashSet and HashTable.

Rules

1.       When hashCode is invoked on the same instance of object more than once, the value returned must consistently be the same integer as long as no information used in equals comparison is modified.
2.       If two objects are equal as per the equals method then the hashCode of both the objects must return the same hashCode.
3.       It is not required that the hashcode values returned by two objects which are not equal as per equals method then be different. But the system can give a better performance if this is adhered to.

Recipe for writing hashCode method

1.       Store some non-zero value say 17 in an int variable say result
2.       For each significant field f in the object do the following:
a.       Compute an int hash code c for the field
                                                   i.      If field is boolean compute (f ? 0 : 1)
                                                 ii.      If field is byte, char, short or int, compute int(f)
                                                iii.      If field is a long, compute (int) (f ^ f >>> 32)
                                               iv.      If field is a float, compute Float.floatToIntBits(f)
                                                 v.      If field is double, compute Double.doubleToLongBits(f) and then has the resulting long as in step iii.
                                               vi.      If the field is a class field and is used for comparison then invoke the hashCode for the object. If object is null use zero. Hashcode must be recursively invoked till objects have no more children
                                              vii.      If the field is an array, then treat as if each element were a separate field.
b.      Combine the hash code c into the result as follows:
                                                   i.      result = 37 * result + c
3.       Return result
4.       Consider caching the hashCode if there is a significant cost in computing this value. But be careful to recompute it if something changes.
5.       Do not be tempted to exclude significant parts of an object from the hash code to improve performance. This will lead to clashes in Hash Maps which will result in worse performance.
Always override toString

The default implementation of toString returns the name of the object and the hashCode which will not make sense. If a good toStringmethod is implemented then one would be able to understand what the object contains making debugging the system easier. toString should include values of all significant fields.
The general contract for toString says that the returned string should be “a concise but information representation that is easy for a person to read”.
It is advisable to document the format of the output that will be generated by the toString method.
All data that is exposed through toString should be available by their corresponding getter methods. If this is not the case then programmers will try to parse the string returned by toString and get the value which is a bad idea.
Override clone judiciously

Clone is like a constructor. The clone method should satisfy the following conditions:
1.       Implement the interface cloneable
2.       Although cloneable does not demand implementation of a clone method it is wise to provide an implementation except for trivial classes which deal with only primitive types.
3.       Consider satisfying the following rules:
a.       x.clone() != x. But this is not necessary.
b.      x.clone.getClass() == x.getClass().But this is not necessary.
c.       x.clone().equals(x). But this is not necessary.
4.       Return the object obtained by super.clone. Do not use constructor
5.       The original object should not be harmed by virtue of being cloned
6.       If required remove the final modifier of certain attributes to get a proper clone
7.       In case of Collections and Maps ensure that every object on the Collection or Map is cloned properly otherwise it will be a shallow copy and this can result in bugs.
8.       Cloneable final classes should not throw CloneNotSupportException from the clone method. This makes it easier to use the class.
9.       Classes that are extendable should implement an empty clone which throws CloneNotSupportException. This will ensure that the subclasses can by default opt out of cloning.
10.   Instead of clone method consider creating a copy Constructor or a static copy method.
public class MyClass {
    public MyClass(MyClass objectToCopyFrom) {
        //Logic to copy the incoming object.
    }
   //or
   public static MyClass getNewInstance(MyClass objectToCopyFrom) {
        //Instantiate new object copy the incoming object.
   }
}

Consider implementing Comparable

Implementing the Comparable interface helps one to leverage several generic algorithms and collection implementations that depend on this interface. sort method in the collections Collection objects like TreeSetetc. can be used.
The compareTo method should return a zero, negative or a positive integer depending on if the object is equal, less than or greater than the current object. If the object types prevent them from being compared it should throw ClassCastException.
The following rules should be followed:
1.       signOf(x.compareTo(y)) == – signOg(y.compareTo(x))
2.       x.compareTo(y) should throw an exception only if y.compareTo(x) also throws an Exception.
3.       It should be transitive. I.e. x.compareTo(y) > 0 and y.compareTo(z) > 0 then x.compareTo(z) > 0
4.       x.compareTo(y) == 0 should imply that signOf(x.compareTo(z)) == signOf(y.compareTo(z)) for all z.
5.       Ideally (x.compareTo(y) == 0) == (x.equals(y)). I.e. if compareTo indicates that the objects are equal then equals should also return true. If this is not true then this should be properly documented.

The next post is about the best practices in writing classes and interfaces.

Advertisements
This will be a series of posts based on the book Effective Java by Joshua Bloch. The posts will be split into the following parts based on the chapters in the book.

  1. Creating and Destroying Objects
  2. Methods Common to All Objects
  3. Classes and Interfaces
  4. Substitutes for C Constructs
  5. Methods
  6. General Programming
  7. Exceptions
  8. Threads
  9. Serialization

This post will cover Creating and Destroying Objects

Providing static factory method

Consider providing static factory methods in the object to create instances of the class.

Benefits

1.       Methods can be named to indicate the type of object that is being returned. This will avoid the problem where it is found necessary to have two constructors having the same signatures, but different parameters.
2.       Factory methods do not need to construct an instance each time they are invoked. This can be useful in limiting the number of object instances of a class created in the JVM. This can be used to create immutable classes. E.g. factory methods the Boolean class can be used to ensure that only two objects of type Boolean ever exist in the JVM. Additional advantage is if a == b also means a.equalsEvery method expects a (b) and the performance of == can be significantly better than ==.
3.       Factory methods need to return the same class, but can return any object which is a subtype of the return type.  This can be used to hide classes in libraries. Only the interface will be exposed to the outside world.

Disadvantages

  1. 1.       Classes without public or protected constructors cannot be subclassed.
  2. 2.       The methods used to create instances of class are not distinguishable from other methods of the class. It can be difficult to identify such methods easily in a class.

Examples

The collections class java.util.Collections can be used to instantiate a variety of Collection classes. The Java Cryptographic Extension uses this method to allow instantiation of the multiple implementation of the APIs.
Enforce Singleton Pattern with a private Constructor

A singleton is a class which has only one instance in the JVM.
There are two ways to create a Singleton
public class FirstSingleton {
    public static final FirstSingleton INSTANCE = new FirstSingleton();
    private FirstSingleton () {
    }
}
public class SecondSingleton {
    private static final SecondSingleton INSTANCE = new SecondSingleton ();
    private SecondSingleton () {
    }
    public static SecondSingleton getInstance() {
        return INSTANCE;
    }
}
The advantage of the second method is that if in a future date one wishes to convert the object from a Singleton to Multiton it will be possible without changing any references to this object. This will be near impossible if one adopts the first pattern.
If we wish to make a Singleton class Serializable then just implementing the interface will not be sufficient, one will need to implement the readResolve method too so that multiple instances of the class are not created.
Enforce noninstantiability with a private constructor

Sometimes there is a requirement for a class which has only static methods and constants. Typically utility classes will fall under this category.
For such classes one should define a private constructor so that this can never be instantiated. If this private constructor is not defined then it will be possible to instantiate this class which does not make sense.
This will also prevent the class from being subclassed.

Examples

1.       java.util.Arrays
2.       java.util.Math
3.       java.util.Collections

Avoid creating duplicate objects

When objects are immutable it makes sense to use the same instance of the object rather than creating a new instance.
E.g.
String s = new String(“silly”); //Don’t do this.
This unnecessarily creates another instance of the String whereas the following would have sufficed.
String s = “silly”;
Any object that needs to contain a constant value should be defined once and reused instead of being instantiated again and again. E.g. fixed dates against which some other dates must be compared.
Note: Creating objects is not very expensive but they should be created only when required or when it makes sense to create objects to keep the code simple and clear.
Maintaining an object pool is a bad idea unless the objects are real heavy weights, i.e. creation of these objects are expensive. One example of such an object is the Database Connection.
Be careful and do not reuse an object which should not be. Typically objects that can change should not be reused, instead a new instance or a copy should be made.

Eliminate obsolete object references             

Objects that are no longer required, but may not be Garbage Collected by virtue of it going out of scope should be nulled so that they can be garbage collected.
Consider the following example which is an implementation of Stack:
public class Stack {
    private Object[] elements;
    private int size = 0;
    public Stack (int initialCapacity) {
        this.elements = new Object[initialCapacity];
    }
    public void push (Object e) {
        ensureCapacity();
        elements[size++] = e;
    }
    public void Object pop() {
        if (size == 0) throw new EmptyStackException();
        return elements[–size];
    }
    /**
     * Ensure space for at least one more element, roughly
     * doubling capacity each time the array needs to grow.
     */
    private void ensureCapacity() {
        if (elements.length == size) {
            Object[] oldElements = elements;
            Elements = new Object[2 * elements.length + 1];
            System.arrayCopy(oldElement, 0, elements, 0, size);
        }
    }
}
On the face of it the object is fine and there are no problems, but there is a hidden memory leak. This happens in the pop method. As the objects are popped out of the stack they are no longer required in the array, but since there is a reference to these objects in the array the garbage collector does not know that these can be collected. This means that even though the Stack has shrunk in size the memory is not getting released and this can cause problems depending on how the object is used.
The way to fix is to nullify the references that are not required as below
    public void Object pop() {
        if (size == 0) throw new EmptyStackException();
        Object result = elements[size];
        elements[–size] = null;
        return result;
    }
This does not mean that one should nullify every object that is not required. This mechanism should be used only under condition as described above, i.e. in scenarios where the Class is managing its own memory without the knowledge of Garbage Collector.
Cache is another source of such leaks. If object in the cache are no longer required, then references to them should be removed. If the reference to the objects needs to cease based on reference to the object outside the scope of the cache then using a WeakHashMap will automatically remove the object from the Map once the reference to the object outside the cache is removed.
If it is not known as to when the object out of scope but it is desired that it be possible to remove objects based on the oldest object then we can use the LinkedHashMap object. This allows for removeEldestEntry which will remove the earliest entry.
Avoid Finalizers

Finalizers are invoked when the object is Garbage collected. It is not known as to when the Garbage Collection will kick off. Given this one should not depend on the finalzer to free resources. Resources should be freed typically in the finally block. If one depends on finalizer to free resources it maybe too late. Expect the clients of the class to invoke the method for freeing of the resources 

The next post is about the best practices in writing methods which are common to all the classes.


Categories

Advertisements