Tuesday, January 26, 2016

Effective Java - Item 8 : Obey the general contract when overriding equals

In this item and the following 4 items the author talks about the non-final methods of the Object class - equals, hashcode, toString, clone. In particular the best ways to override them: when? and how?

The default implementation of the equals() method uses the "==" relation to compare two objects. Java docs states that "for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true)."

Let us look at the following scenario. I created a custom String class MyString which accepts a String as a parameter. I also created 2 instances of this MyString and passed the same string literal "hello" and compared them using the equals method.


What do you think will be the output?

From what we have learnt about the equals method it is obvious that the output will be false.
Then we might as well have used the "==" operator to check their equivalence.

But what if we wanted to check the logical equivalence of the two strings. In that case both of them should be equal. This is where the equals method comes into picture. You can override the equals method and give your own implementation to check the logical equivalence of the MyString objects.

I gave this example of providing a custom wrapper around the String class just to illustrate the application of overriding the equals method.

So, now that we learnt in what scenario we would want to override the equals method the next step is to learn how to do it.
The author says that there is a written contract with a set of four rules and one must adhere to it when overriding the equals method.

1.     Reflexivity

The first requirement says that the object whose equal method you are overriding must maintain the reflexive property, i.e. the object must be equal to itself.
Considering the MyString class from our previous example, the following code must output true according to this requirement.
1
2
MyString a = new MyString("Hello");
System.out.println(a.equals(a));

     2.     Symmetry

The second requirement says that for two non-null references the equal method of either of the two references should return true iff the equals method of both the references return true. To better understand what this exactly means let us build upon our MyString class and consider that we override the equals method to make it case-insensitive. The following code should output true.
1
2
3
MyString a = new MyString("Hello");
MyString b = new MyString("hello");
System.out.print(a.equals(b));
But what about in this scenario?
1
2
3
MyString a = new MyString("Hello");
String b = new String("hello");
System.out.print(a.equals(b));
It should output false else it would violate the symmetric property, because b.equals(a) will be false.
And why is that so, you ask?
Think about it, we are only overriding the equals method of MyString class. The String class does not have a clue that we are trying to compare two strings irrespective of their case.

    3.     Transitivity

The third requirement says the equals method should uphold the transitive property i.e. if any two one object is equal to a second object and the second is equal to a third object then the first object should also be equal to the third object. If the first object is not equal to the third then the equivalence of the first and the second should return false.
The author says that this requirement can usually be violated when an instantiable class is extended and the sub-class adds a new property.
So let us extend our MyString class and add a color property.
1
2
3
4
5
6
7
class MyColorString extends MyString {
    private final Color strColor;
    public MyColorString(String string, Color color) {
      super(string);
      this.strColor = color;
    }
}
Now consider the following three objects
1
2
3
MyColorString str1 = new MyColorString("hello", Color.RED);
MyString str2 = new MyString("hello");
MyColorString str3 = new MyColorString("hello", Color.BLUE);
These three strings can be compared using the equals method since it should use the instanceof operator (we will see this later  in the conclusion) and str1 is an instance of the parent class MyString.
So if we do a comparison of the first 2 instances and second and third as:
1
2
str1.equals(str2);
str2.equals(str3);
Both of them will return true as they do not consider the color property of the sub-class. But str1.equals(str3) will return false as now the color property comes into account. This clearly violates the transitivity contract.
A workaround to this problem could be to use getClass() method instead of instanceof operator in the equals method to ensure equating objects of the same implementation class.
But is this a good solution? Well, according to the author it is not since it violates the Liskov substitution principle and that is an entire topic in itself. The author says that: "There is no way to extend and instantiable class and add a value component while preserving the equals contract". 

    4.     Consistency

The condition of this rule is quite simple. It says that the equality of any two objects should be consistent i.e. their equality should hold true unless one of them is modified. Here we need to consider if the object we are dealing with is mutable or immutable, because mutable objects can be modified and so they can be equal to different objects at different times but that is not the case with immutable objects.
The author brings up an important and interesting question here, whether to make a class mutable or immutable? He asks the reader to think hard about this question. Infact he has an item dedicated to this question further in the book.
Another important point we need to keep in mind when overriding the equals method is not to depend on unreliable sources such as network access. A good example of this is the equals method of java.net.URL

    5.     Non-nullity

The name of this rule is actually devised by the author. As the name suggests according to this requirement for any object x the following should not return true: x.equals(null) .

To conclude you can use the following as a recipe for a well implemented equals method:


So here is an implementation of our MyString class with the equals method


Saturday, January 16, 2016

Effective Java - Item 7 : Avoid finalizers!

Unpredictable! Dangerous! Unnecessary! The author Joshua Bloch makes sure to set the tone of the item right in the beginning by using these adjectives. To augment this view point on finalizers he says that it can be used as a rule of thumb to avoid them altogether. That is a pretty strong point of view.

Just to give you some background the finalize() method in Java is a special method of the class Object. It will be invoked by the garbage collector (GC) on an object when GC determines that there are no more references to the object i.e. just before GC reclaims the object. It sounds something similar to the destructor in C++, right? So, can it be used to cleanup any external resources like closing files? 

Wrong!

The author gives the following 3 reasons to support his point of view:

  • The promptness of the finalize methods' execution is not guaranteed.
  • There is a severe performance penalty for using finalizers.
  • Uncaught exceptions thrown by finalize method is ignored, and finalization of that object terminates.
The promptness of the finalize methods' execution is not guaranteed

The author says that there is no fixed time interval between the time an object becomes eligible for garbage collection and the time its finalizer is executed. It is dependant on the garbage collection algorithm which varies according to the JVM implementation. Further he adds that there is no guarantee that the finalize method will ever get executed at all. So now you can imagine how dangerous it is to depend on finalizers.
I was curious to find out because I have never actually made use of the finalize method in any of my code. So I wrote a small program to test it.

Here I have one class which had a FileInputStream object and a method which tries to open a file. I have overridden the finalize method where I close the file. I have added one print statement in each block (try-catch-finally-finalize) to better understand the flow of execution. I have also created another class which will create an instance of this class, invoke the openFile method and further nullify the reference to make the instance eligible for garbage collection.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.io.FileInputStream;
import java.io.FileNotFoundException;


public class OpenFileTest {
 
 private FileInputStream file = null;

 public void openFile(String filename) {
  try {
   System.out.println("Inside try block");
   file = new FileInputStream(filename);
  } catch (FileNotFoundException e) {
   System.out.println("Inside catch block");
   System.err.println("Could not open file "+filename);
  } finally {
   System.out.println("Inside finally block");
  }
 }
 
 @Override
 protected void finalize() throws Throwable {
  System.out.println("Inside finalize method");
  if(file!=null){
   file.close();
   file = null;
  }
  super.finalize();
 }
}

1
2
3
4
5
6
7
8
9
public class TestFinalizeMain {

 public static void main(String[] args) {
  OpenFileTest o = new OpenFileTest();
  o.openFile("C:\\shreyas\\CusumCalc\\cusum_output.csv");
  o = null;
 }

}

Output:
Inside try block
Inside finally block

Wait, that is not what was supposed to happen. What happened to the finalize method being called by our garbage collector?
So, this is exactly what the author was talking about.

Here we can see that the finally block did get executed without any issues. So using finally block to deallocate resources is a safe bet.
There are other ways of forceful execution of the finalize method like System.gc(), but then again it just increases the odds and does not provide any guarantee.

There is a severe performance penalty for using finalizers.

The author says that the time to create and destroy an object on his machine apparently increased by 430 times when he used finalizer. Well, if you come to think of it this is actually true. The work of the garbage collector is to scan the heap often and determine which objects are no longer referenced and de-allocate the memory. But if an object uses a finalizer then the garbage collector is interrupted. And it so happens that finalizers are processed on a thread that is given a fixed, low priority. So objects that are otherwise eligible for garbage collection will be pending on finalization and use up all the available memory causing your application to slow down.

Uncaught exceptions thrown by finalize method is ignored

Now here is another good reason why you are better off without a finalizer. Any uncaught exception thrown during a finalization is ignored and it will not be propagated further and the finalization halts. Java handles uncaught exceptions by terminating the thread and usually printing the stack trace to the console. But in this case there will be no warning by means of any message being printed.

So, what is a good alternative if you want to do all your resource releasing work then? The author suggests providing an explicit termination method and requiring the clients of the class to invoke this method on each instance when it is no longer needed would be your best bet. Some good examples of such methods are the close methods on InputStream, OutputStream, and java.sql.Connection
Another option I could think of is to use the try-with-resources statement provided starting from Java 7.

Now all that said and done the question remains as what is a real good use of the finalize method? 
In some rare cases if the user forgets to call the explicit termination method of an object then a finalizer can be used as an extra level of safety to free the resource, better late than never. But the author says that it is better to use safety and precaution if you must use it and one way he suggests is to add the finalization code in a try block when you override the finalize method and invoke the super class finalizer (super.finalize()) in the finally block. This is because "finalizer chaining" is not performed automatically.

A final thought - You are better off without a finalizer!