Java Secret: Double Brace Initialization

Overview

Java allow you to add initialisation blocks to a class definition. This is particularly useful in combination with anonymous classes for creating mock collections. esp for unit tests.

Initializer Blocks

In a class you can add blocks of code which are called during class initialisation. This are not widely used because they tend to be confusing. IMHO.

public class A {
    private int version; {
        String versionText = System.getProperty("A.version", "-1");
        try {
            version = Integer.parseInt(System.getProperty("A.version", "-1"));
        } catch (NumberFormatException nfe) {
            System.err.println("Unable to parse A.version "+versionText);
            version = -1;
        }
    }
}
In this class, the initialisation of version is too complex to write in one statement. An initializer block has been used which is section of code which is added to the constructor (in this case, the default constructor)

As this a rarely used, it is rather confusion and a clearer approach is to use a method call. The method call itself is still added to the constructor, but it is clearer as to what it is doing.
public class A {
    private int version = initVersion();

    private static int initVersion() {
        String versionText = System.getProperty("A.version", "-1");
        try {
            return Integer.parseInt(System.getProperty("A.version", "-1"));
        } catch (NumberFormatException nfe) {
            System.err.println("Unable to parse A.version "+versionText);
            return -1;
        }
    }
}

Double Brace Initialization


For collections

This initializer block notation is available to anonymous classes and it is particularly useful in creating mock data in unit tests.
Map<String, String> directions = new LinkedHashMap<String, String>() {{
    put("N", "north");
    put("S", "south");
    put("E", "east");
    put("W", "west");
}};
This is not a special {{ symbol but two { given different meanings. The first { shows this is an anonymous class, the second { means the start of an initializer block.

For GUI containers

This example is from the link below.
add(new JPanel() {{
 setLayout(...);
 setBorder(...);
 add(new JLabel(...));
 add(new JSpinner(...));
 }});

For creating mock objects

This example is from the jMock cookbook
context.checking(new Expectations() {{
    oneOf (clock).time(); will(returnValue(loadTime));
    oneOf (clock).time(); will(returnValue(fetchTime));
            
    allowing (reloadPolicy).shouldReload(loadTime, fetchTime); will(returnValue(false));
        
    oneOf (loader).load(KEY); will(returnValue(VALUE));
}});

Downsides

  • This can appear to be an obscure notation. Thank you, @PeterStibrany
  • In some cases there is more obvious way to build a list. e.g. Arrays.asList(...). Thank you @trollhorn.
  • An anonymous class created in a non-static content will have a reference to the parent even if not used. This can be a source of memory leaks. Thank you @Jelmer, (If you create it in a static method it won't happen)
  • Depending on how equals() is implemented, an anonymous sub-class might not be equals to the base type. If equals uses instanceof and many collections do, there isn't a problem. Thanks @Eric Jablow

Reference

Double Brace Initialization jMock Cookbook

Comments

  1. Cheers. You are welcome to read my other posts as well. ;)

    ReplyDelete
  2. Be careful though, anonymous inner classes hold a reference to its parent class. So when misused this can be a source of memory leaks

    ReplyDelete
  3. @Jelmer, Good point. I will include that in the article.

    ReplyDelete
  4. An anonymous inner class with an instance initializer is still distinct from its base class. If the equals() method checks for class equality, instead of using instanceof checks, then the anonymous instance will never be equal to anything else.

    Foo a = new Foo() {{setBar(3);}};
    Foo b = new Foo(); b.setBar(3);
    System.out.println(a.equals(b)); // prints false.

    ReplyDelete
  5. Nice article! i guess it would be better to have method that will take varargs as a parameter and return the result of Collection type.

    ReplyDelete
  6. @Eric Jablow, I have added your comment to disadvantages. Most collections use instanceof rather than getClass() == but it could be a problem.

    ReplyDelete
  7. @Shubhashish Bhowmik, I agree, you can use Arrays.asList(Object...) to create a List from a vararg. You can write your own similar method for Maps.

    However for more complex objects, its not so simple.

    ReplyDelete

Post a Comment

Popular posts from this blog

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable

System wide unique nanosecond timestamps