There is this so called „double brace“ pattern for initializing collection. We will see if it should be a pattern or an anti-pattern later on…
The idea is that we should consider the whole initializion of a collection one big operation. In other languages we write something like
[element1 element2 element3]
or
[element1, element2, element3]
for array-like collections and
{key1 val1, key2 val2, key3 val3}
or
{key1 => val1, key2 => val2, key3 => val3}
.
Java could not do it so well until Java 9, but actually there was a way to construct sets and lists:
Arrays.asList(element1, element2, element3);
or
new HashSet<>(Arrays.asList(element1, element2, element3));
.
Do not ask about immutability (or unmodifyability), which is not very well solved in the standard java library until now, unless you are willing to take a look into Guava, which we will in another article… Let us stick with Java’s own facilities for today.
So the double brace pattern would be something like this:
import java.util.*;
public class D {
public static void main(String[] args) {
List<String> l = new ArrayList<String>() {{
add("abc");
add("def");
add("uvw");
}};
System.out.println("l=" + l);
Set<String> s = new HashSet<String>() {{
add("1A2");
add("2B707");
add("3DD");
}};
System.out.println("s=" + s);
Map<String, String> m = new HashMap<String, String>() {{
put("k1", "v1");
put("k2", "v2");
put("k3", "v3");
}};
System.out.println("m=" + m);
}
}
What does this do?
First of all having an opening brace after the new XXX()
creates an anonymous class extending XXX. Then we open the body of the extended class. What is well known to many is that there can be a static {....}
section, that is called exactly once for each class. The same applies for a non-static section, which is achieved by omitting the static
keyword. This is of course called once for each instance of the class, so in this case it will be called after the constructor of the base class and serves kind of as a replacement for the constructor. To make it look cooler the two pairs of braces are placed together.
It is not so magic, but it creates a lot of overhead by creating anonymous classes with no real additional functionality just for the sake of an initialization. It is even worse, because these anonymous inner classes are not static, so they actually can refer to their surrounding instance. They do not make use of this, but anyway they carry a reference to their surrounding class which might be a very serious problem for serialization, if that is used. And for garbage collection. So please consider the double-brace-initialization as an anti-pattern. Others have blogged about this too…
There are more legitimate ways to group the initialization together. You can put the initialization into a static method and call that. Or you could group it with single braces, just to indicate the grouping. This is a bit unusual, but at least correct:
import java.util.*;
public class E {
public static void main(String[] args) {
List<String> l = new ArrayList<String>();
{
l.add("abc");
l.add("def");
l.add("uvw");
}
System.out.println("l=" + l);
Set<String> s = new HashSet<String>();
{
s.add("1A2");
s.add("2B707");
s.add("3DD");
}
System.out.println("s=" + s);
Map<String, String> m = new HashMap<String, String>();
{
m.put("k1", "v1");
m.put("k2", "v2");
m.put("k3", "v3");
}
System.out.println("m=" + m);
}
}
While the first two can somehow be written using Arrays.asList(...)
, now in Java 9 there are nicer ways for writing all three using List.of("abc", "def", "uvw");
, Set.of("1A2", "2B707", "3DD");
and Map.of("k1", "v1", "k2", "v2", "k3", "v3");
, which is recommended over any other way because there are some additional runtime and compile time checks and because these are efficient immutable collections. This has been blogged about too.
The aspect of immutability which we should consider today, is not very well covered by the java collections (apart from the new internal one for the new factory methods. Wrapping in Collections.unmodifyableXXX(...)
is a bit of overhead in terms of code, memory and CPU-usage but it does not give a guarantee that the collection wrapped into this is actually not being modified elsewhere.