Java Quickies – Double braces initialization

Double braces initialization

Today, we are going to discuss a special case. It is called the “double braces initialization”.

What is that?
Usually when you start learning Java, you try to minimize everything, try to find the most concise syntax. I know one thing that used to upset me is the inability to declare and fill my List/Map using a single statement. Indeed, we are forced to do:

List<String> myList = new ArrayList();
myList.add("Element1");
myList.add("Element2");

Actually there is a way to do this using one line. I will show it to you and explain why you must not use it EVER.

private List<String> myList = new ArrayList<String>() {{
	add("Element1");
	add("Element2");
}};

Pretty simple, right? Now you get why it is called the double braces initializer. But do you understand why this syntax work or even compile?

Let’s look at what javac generated. For this example, I used a class named TestDeclaration in which I used the double braces initializer. I can see the compiler generated a TestDeclaration.class file, that is normal, but following this file is another one named TestDeclaration$1.class. Not that normal, isn’t it?

Let’s investigate a little bit more:

package test;
public class TestDeclaration {
    private List<String> myList = new ArrayList<String>() {{
		add("Element1");
		add("Element2");
    }};

    public List<String> getMyList() {
        return myList;
    }

    public static void main(String[] args) {
        TestDeclaration testDeclaration = new TestDeclaration();
        List<String> myList = testDeclaration.getMyList();
        System.out.println(myList.getClass());
    }
}

The expected result of line 15 is the class java.util.ArrayList but the actual result is: “class test.TestDeclaration$1”. So the double braces initializer has transformed my ArrayList to another class named TestDeclaration$1! But wait, there is more:

System.out.println(myList.getClass().getEnclosingClass());

The method getEnclosingClass returns the enclosing class if the class is an inner class. It returns null otherwise. The result of this line is: “class test.TestDeclaration”.

Let’s sum up what we have seen so far:

  • The declaration created an inner class inside TestDeclaration called TestDeclaration$1
  • The declaration initialize the content using add calls

Explanation of the “double braces initialization”

Do you remember your early swing development in Java? You most likely used the ActionListener on JButton. As a refresher, here is what it looks like:

JButton button = new JButton();
button.addActionListener(new ActionListener() {
	@Override public void actionPerformed(ActionEvent e) {
		// My action handled
	}
});

Do you recognize the double braces initializer syntax? This is exactly the same thing. We are extending the ArrayList class and using an anonymous initialization block, we call the add method (which we have access because we extend ArrayList) to add our elements. If you do not know about anonymous initialization blocks, these are blocks executed at the same time as the constructor. We can write this (people usually know this syntax more using the static keyword):

public class TestDeclaration {
    {
        myInt = 25;
    }
	
    private int myInt;

    public int getMyInt() {
        return myInt;
    }
}

After the constructor is executed, myInt will be equal to 25, exactly as if this block would be inside a constructor. Anyway, you should never use the syntax as it does nothing more a constructor could and may only lead to errors and mistakes.

One last thing you need to know about this double braces initialization. Check this code:

TestDeclaration testDeclaration = new TestDeclaration();
List<String> myList = testDeclaration.getMyList();
try {
	ByteArrayOutputStream out = new ByteArrayOutputStream();
	ObjectOutputStream os = new ObjectOutputStream(out);
	os.writeObject(myList);
	os.close();
	System.out.println("Success");
} catch (IOException e) {
	System.err.println("Failed");
	e.printStackTrace();
}

What will be the result? Success or Failed? Well the result is Failed!

java.io.NotSerializableException: test.TestDeclaration
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1547)
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1508)
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
	at test.TestDeclaration.main(TestDeclaration.java:39)

The sub class of List being a non static inner class of TestDeclaration, it maintains a pointer to its enclosing class and will try to serialize it! Fortunately, TestDeclaration is not Serializable and we can see the error. Another consequence is that as long as the List is referenced inside the application, the class TestDeclaration cannot be garbage collected.

Here is the file used during the article. And keep in mind, multi lines initialization may be longer but are not that bad in the end 😉

package test;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

public class TestDeclaration {
    private List<String> myList = new ArrayList<String>() {
        {
            add("Element1");
            add("Element2");
        }
    };

    {
        myInt = 25;
    }

    private int myInt;

    public int getMyInt() {
        return myInt;
    }

    public List<String> getMyList() {
        return myList;
    }

    public static void main(String[] args) {
        TestDeclaration testDeclaration = new TestDeclaration();
        List<String> myList = testDeclaration.getMyList();
        System.out.println(myList.getClass());
        System.out.println(myList.getClass().getEnclosingClass());
        try {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream os = new ObjectOutputStream(out);
            os.writeObject(myList);
            os.close();
            System.out.println("Success");
        } catch (IOException e) {
            System.err.println("Failed");
            e.printStackTrace();
        }

        System.out.println(new TestDeclaration().getMyInt());
    }

}

Leave a Comment