What Is Double Brace Initialization in Java

What is Double Brace initialization in Java?

Double brace initialisation creates an anonymous class derived from the specified class (the outer braces), and provides an initialiser block within that class (the inner braces). e.g.

new ArrayList<Integer>() {{
add(1);
add(2);
}};

Note that an effect of using this double brace initialisation is that you're creating anonymous inner classes. The created class has an implicit this pointer to the surrounding outer class. Whilst not normally a problem, it can cause grief in some circumstances e.g. when serialising or garbage collecting, and it's worth being aware of this.

Efficiency of Java Double Brace Initialization?

Here's the problem when I get too carried away with anonymous inner classes:

2009/05/27  16:35             1,602 DemoApp2$1.class
2009/05/27 16:35 1,976 DemoApp2$10.class
2009/05/27 16:35 1,919 DemoApp2$11.class
2009/05/27 16:35 2,404 DemoApp2$12.class
2009/05/27 16:35 1,197 DemoApp2$13.class

/* snip */

2009/05/27 16:35 1,953 DemoApp2$30.class
2009/05/27 16:35 1,910 DemoApp2$31.class
2009/05/27 16:35 2,007 DemoApp2$32.class
2009/05/27 16:35 926 DemoApp2$33$1$1.class
2009/05/27 16:35 4,104 DemoApp2$33$1.class
2009/05/27 16:35 2,849 DemoApp2$33.class
2009/05/27 16:35 926 DemoApp2$34$1$1.class
2009/05/27 16:35 4,234 DemoApp2$34$1.class
2009/05/27 16:35 2,849 DemoApp2$34.class

/* snip */

2009/05/27 16:35 614 DemoApp2$40.class
2009/05/27 16:35 2,344 DemoApp2$5.class
2009/05/27 16:35 1,551 DemoApp2$6.class
2009/05/27 16:35 1,604 DemoApp2$7.class
2009/05/27 16:35 1,809 DemoApp2$8.class
2009/05/27 16:35 2,022 DemoApp2$9.class

These are all classes which were generated when I was making a simple application, and used copious amounts of anonymous inner classes -- each class will be compiled into a separate class file.

The "double brace initialization", as already mentioned, is an anonymous inner class with an instance initialization block, which means that a new class is created for each "initialization", all for the purpose of usually making a single object.

Considering that the Java Virtual Machine will need to read all those classes when using them, that can lead to some time in the bytecode verfication process and such. Not to mention the increase in the needed disk space in order to store all those class files.

It seems as if there is a bit of overhead when utilizing double-brace initialization, so it's probably not such a good idea to go too overboard with it. But as Eddie has noted in the comments, it's not possible to be absolutely sure of the impact.


Just for reference, double brace initialization is the following:

List<String> list = new ArrayList<String>() {{
add("Hello");
add("World!");
}};

It looks like a "hidden" feature of Java, but it is just a rewrite of:

List<String> list = new ArrayList<String>() {

// Instance initialization block
{
add("Hello");
add("World!");
}
};

So it's basically a instance initialization block that is part of an anonymous inner class.


Joshua Bloch's Collection Literals proposal for Project Coin was along the lines of:

List<Integer> intList = [1, 2, 3, 4];

Set<String> strSet = {"Apple", "Banana", "Cactus"};

Map<String, Integer> truthMap = { "answer" : 42 };

Sadly, it didn't make its way into neither Java 7 nor 8 and was shelved indefinitely.


Experiment

Here's the simple experiment I've tested -- make 1000 ArrayLists with the elements "Hello" and "World!" added to them via the add method, using the two methods:

Method 1: Double Brace Initialization

List<String> l = new ArrayList<String>() {{
add("Hello");
add("World!");
}};

Method 2: Instantiate an ArrayList and add

List<String> l = new ArrayList<String>();
l.add("Hello");
l.add("World!");

I created a simple program to write out a Java source file to perform 1000 initializations using the two methods:

Test 1:

class Test1 {
public static void main(String[] s) {
long st = System.currentTimeMillis();

List<String> l0 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};

List<String> l1 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};

/* snip */

List<String> l999 = new ArrayList<String>() {{
add("Hello");
add("World!");
}};

System.out.println(System.currentTimeMillis() - st);
}
}

Test 2:

class Test2 {
public static void main(String[] s) {
long st = System.currentTimeMillis();

List<String> l0 = new ArrayList<String>();
l0.add("Hello");
l0.add("World!");

List<String> l1 = new ArrayList<String>();
l1.add("Hello");
l1.add("World!");

/* snip */

List<String> l999 = new ArrayList<String>();
l999.add("Hello");
l999.add("World!");

System.out.println(System.currentTimeMillis() - st);
}
}

Please note, that the elapsed time to initialize the 1000 ArrayLists and the 1000 anonymous inner classes extending ArrayList is checked using the System.currentTimeMillis, so the timer does not have a very high resolution. On my Windows system, the resolution is around 15-16 milliseconds.

The results for 10 runs of the two tests were the following:

Test1 Times (ms)           Test2 Times (ms)
---------------- ----------------
187 0
203 0
203 0
188 0
188 0
187 0
203 0
188 0
188 0
203 0

As can be seen, the double brace initialization has a noticeable execution time of around 190 ms.

Meanwhile, the ArrayList initialization execution time came out to be 0 ms. Of course, the timer resolution should be taken into account, but it is likely to be under 15 ms.

So, there seems to be a noticeable difference in the execution time of the two methods. It does appear that there is indeed some overhead in the two initialization methods.

And yes, there were 1000 .class files generated by compiling the Test1 double brace initialization test program.

Java double brace initialization vs lambda

Lambda expressions are different from an anonymous inner class that happens to implement a functional interface.

Anonymous inner classes will create their own class file at compilation, usually something along the lines of Foo$1.class, if it's contained in the Foo class. It is a fully functional class that implements an interface or subclasses a class. To reference local values outside its scope, it will, behind the scenes, create an instance variable in the anonymous inner class that represents a copy of the value. This is why the variable must be effectively final -- otherwise the actual variable may change and the copy may be stale.

Lambda expressions don't create anonymous inner classes. They use a java.lang.invoke.LambdaMetafactory that produces a CallSite that can be used later to execute the lambda expression. The lambda expression, whether it's a block or an expression, gets converted to a hidden private static method within the class in which it's contained. Instead of creating a class with a hidden variable, captured values get translated into parameters of the hidden private static method. Local values still must be effectively final because the value passed to the method is again a copy. The method gets invoked by a invokedynamic instruction in the JVM.

Sources:

  • How Lambdas And Anonymous Inner Classes Work
  • PART 4 – HOW LAMBDA EXPRESSION INTERNALLY WORKS

Alternative to double brace initialization

Instead of:

new ArrayList<String>() {{ add("Y"); add("Upload Success"); }}

you may use:

Arrays.asList("Y", "Upload Success")

This gives you a fixed-size list. If you want to be able to add or remove elements later, convert it into an ArrayList:

new ArrayList<>(Arrays.asList("Y", "Upload Success"))

And of course you can put this list into its own variable before putting it into your map structure.

If you want to put either [Y, Upload Success] or [N, Upload Failed] and make sure the lists aren’t shared between map entries, here’s a suggestion: First, outside your loop:

final List<String> successList = Arrays.asList("Y", "Upload Success");
final List<String> failureList = Arrays.asList("N", "Upload Failed");

Then inside your loop:

if (wasSuccessful) {
errorList.put(tempName,
Collections.singletonMap("upl", new ArrayList<>(successList)));
} else {
errorList.put(tempName,
Collections.singletonMap("upl", new ArrayList<>(failureList)));
}

You may take the idea one step further and build the maps outside the loop. And again, if you want the inner map to be a HashMap, just convert into one: new HashMap<>(Collections.singletonMap("upl", new ArrayList<>(successList))).

You notice I have avoided the double brace initialization completely. While it’s brief, it has an overhead both conceptually and performancewise. You are creating a new anonymous subclass each time, which I don’t find warranted.

How do I fix these double braces

To understand how to fix it, you should first understand what it's doing. For this, you'll need to know about two things:

  1. Anonymous sub-classes
  2. Instance initializer blocks

The TL;DR of how to fix it is just to split those put calls and initialization out from the setter:

Map<String, String> valueMap = new LinkedHashMap<String, String>();
valueMap.put("0", "None");
valueMap.put("1", "Subtle");
valueMap.put("2", "Intrusive");
notifyTypeItem.setValueMap(valueMap);

Read on for an explanation of what's happening and why it can be a bad approach.

Anonymous Sub-classes

An anonymous class is generally just a class without a name. For example, you can create anonymous instances of an interface like Runnable:

Runnable r = new Runnable() {
@Override
public void run() {
// Do something
}
};
r.run(); // Does that something

Similarly, you can create anonymous instances of abstract and concrete classes too. For example, it's very common to create an anonymous instance of ThreadLocal:

private static ThreadLocal<SimpleDateFormat> localIsoDateFormat = new ThreadLocal<SimpleDateFormat>() {

@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
}

This is useful when you don't need a full, dedicated class to override a method or two, similar to creating anonymous instances of interfaces with only one method.

Instance Initializer Blocks

An instance initializer block allows you to execute initializers outside of your constructor. For example:

public class MyClass {

private final String s;
{
s = "My Class String";
}

public String getS() { return s; }
}

It's essentially a stand-in for a constructor, and it's generally unnecessary, so you rarely see it. It can almost always be moved to a constructor instead.

Combining Them

Your example combines them. It's creating an anonymous subclass of LinkedHashMap, then it's also using an initializer block. More properly formatted, your code is:

Map<String, String> map = new LinkedHashMap<>() {
{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}
};

It's an anonymous instance of LinkedHashMap with an instance initializer block that does the put calls.

Why is it bad?

For the same reason that you need to be careful creating anonymous classes: references to the enclosing class instance.

Anonymous classes are notorious for being the source of memory leaks in your application. Your code appears to be in a non-static context. This means that the anonymous LinkedHashMap subclass you create will have an implicit reference to the class that your method is sitting in. For example, if your method is in MyClass:

public class MyClass {
private SelectItem notifyTypeItem = new SelectItem();

public void foo() {
notifyTypeItem.setTitle("Default Notification");
notifyTypeItem.setWidth("100%");
notifyTypeItem.setValueMap(new LinkedHashMap<String, String>() {{
put("0", "None");
put("1", "Subtle");
put("2", "Intrusive");
}}
);
}
}

The newly created LinkedHashMap subclass (MyClass$1 will be the "class name", if you can call it such a thing) will have a reference to the enclosing MyClass instance. In some cases, this may be fine. But if you're creating the notifyTypeItem with the intention of passing it to something else and throwing away your MyClass instance, then you'll be creating a memory leak in your application. The MyClass instance will be referenced by the MyClass$1 instance, and the SelectItem will reference the MyClass$1 instance, so the MyClass instance will never be garbage collected until the SelectItem instance is no longer referenced. If MyClass has several other references besides the SelectItem, then that will only increase the total memory a single MyClass instance consumes, and lead to more memory leak issues.



References

Here are some relevant links:

  • Anonymous Classes Java Trail (Java trail, docs.oracle.com)
  • Initializing Fields Java Trail (Java trail, docs.oracle.com)
  • What is Double Brace initialization in Java? (SO question, stackoverflow.com)
  • Why you shouldn't use the double brace initializer (blog post by Nish Tahir, blog.nishtahir.com)
  • When exactly is it leak safe to use (anonymous) inner classes? (SO question, stackoverflow.com)

Java double brace initialization works always?

There is a detail you left out that explains this.

First of all, let's review steps 3 through 5 of the initialization procedure (summarized):

3. the superclass constructor is called

4. the instance initializers are called

5. the body of the constructor is called

The detail that you've left out is that your expression is not simply creating a new instance of the HashSet class, it is in fact creating a new instance of an anonymous subclass of HashSet. (I believe this is specified in section 15.9.1.)

Since you did not declare a constructor, the default constructor is used. But before that, the constructor of the superclass HashSet has completed.

So, in summary, the HashSet constructor completes before your initializer block runs.

Meaning of new Class(...){{...}} initialization idiom

It's called double curly brace initialization. (EDIT: Link removed, archived here)

It means you're creating an anonymous subclass and the code within the double braces is basically a constructor. It's often used to add contents to collections because Java's syntax for creating what are essentially collection constants is somewhat awkward.

So you might do:

List<String> list = new ArrayList<String>() {{
add("one");
add("two");
add("three");
}};

instead of:

List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

I actually don't like that and prefer to do this:

List<String> list = Arrays.asList("one", "two", "three");

So it doesn't make much sense in that case whereas it does for, say, Maps, which don't have a convenient helper.



Related Topics



Leave a reply



Submit