Sunday, 23 May 2010

Instance Initializers in Java

Take a look at this simple code

Code Snippet 1

public class Init {
  {
    System.out.println("In the beginning was the command line");
  }

  public Init()
  {
    System.out.println("Created an instance");
  }

  public static void main(String[] args)
  {
    Init init = new Init();
  }
}

What do you think the output is? It's this -
    In the beginning was the command line
    Created an instance

The 'hanging' braces at the start of the class definition are instance initializers. Most of us are more familiar with static initializers -

Code Snippet 2
static
{
  //Do stuff
}


Instance initializers (II) are not seen often in everyday Java code - so they might seem odd at first. They are executed every time an instance of that class is created, before the statements in the constructor are executed. (See The Java Language Specification 3 section 8.6).

One use of IIs can be to execute something whenever an instance is created, and the class has multiple constructors, without calling it in every single constructor.
Another one which has become popular is populating collections during declaration, in the style of Ruby or Python single-line initializers -

Code Snippet 3
private Set<String> names = new HashSet<String>() {
  {
    add("Rigel");
    add("Vega");
    add("Antares");
  }
};


This idiom was how I encountered IIs first while reading somebody's blog.
What is actually happening here?
  1. An anonymous inner class is created.
  2. An instance initializer block is added to the anon inner class.
  3. Objects are added to the instance of that class when the names variable is initialized.

Now take this scenario:

Code Snippet4
public class WrongUsage {

  private Set<String> names;
  {
    add("pleiades");
  }

  public void WrongUsage()
  {
    names = new HashSet<String>();
  }

  public void add(String name)
  {
    names.add(name);
  }
}

Based on what we have seen above, the names set is used before it's initialized. So this throws a NullPointerException.
Let's take another case - similar to the above but involving inheritance.

Code Snippet 5

public class MyHashSet extends HashSet {
  {
    add("pleiades");
    System.out.println("Added");
  }

  public MyHashSet()
  {
    super();
    System.out.println("After calling super");
  }

  public static void main(String[] args)
  {
    Set set = new MyHashSet();
  }
}


This runs, with the output being
    Added
    After calling super

In this case, add() internally uses the inner HashMap inside HashSet which is initialized in the HashSet constructor. This implies that the instance initializer is invoked before the class constructor, but after the superclass constructor (The super call is redundant here. It will be called anyway).

So the sequence is
  1. Superclass initialization (this includes superclass instance initializers and constructor)
  2. Current class's Instance initializers
  3. Current class's Constructor

This is why the code in Code Snippet 3 does not throw an NPE - because it's a case of inheritance (the anon inner class is a subclass of HashSet)

1 comment:

  1. nice one :)

    so question: for snippet 3 why bother with the initializer block when you can just put the code in directly and have an anonymous class created with that as the def constructor.

    ReplyDelete