Double brace initialization is a Java idiom to initialize a Collection like a list, set, and map at the time of declaration. At times, you need a list of fixed elements e.g. supported products, supported currencies, or some other config, and on-the-spot initialization reduces the line of code and improves readability. Double brace initialization idiom becomes popular because there is no standard way to create and initialize Collection at the same time in Java. Unfortunately, unlike other JVM languages like Scala and Groovy, Java doesn't support collection literals yet.
Due to this limitation, creating an unmodifiable List with small numbers of elements requires many lines of code involving creating a list, repeatedly calling add() method to add those elements, and then finally wrapping it into the unmodifiable list as shown below :
This is quite verbose, and because it cannot be expressed in a single expression, static lists must be populated in static initializer blocks rather than via a more convenient field initializer.
Double brace initialization idiom can do this in just one line as shown below:
You can also initialize HashMap with values using double brace initialization as following:
This looks pretty, but it has its own set of disadvantages, which made it an anti-pattern, we'll see them in the next section.
Btw, In order to best understand design patterns, you need to work out some scenarios, examples, etc. It's best to get this kind of knowledge as part of your work but even if you don't get there, you can supplement them by joining a comprehensive course like these best Java design pattern courses and doing some object-oriented software design exercises.
Pros:
1) Less line of code
2) Creation and Initialization in the same expression
Cons:
2) Obscure, internally uses the instance initializer construct of the anonymous class.
2) It cost an extra class every time you use it
3) It holds a hidden reference to the enclosing instance, which may cause memory leaks.
Due to its disadvantage and better alternative double brace initialization is now considered as an anti-pattern in the Java world.
List<Integer> list = new ArrayList<>(); list.add(2); list.add(3); list.add(5); list.add(7); List<Integer> unmodifiableList = Collections.unmodifiableList(list);
This is quite verbose, and because it cannot be expressed in a single expression, static lists must be populated in static initializer blocks rather than via a more convenient field initializer.
Double brace initialization idiom can do this in just one line as shown below:
List<Integer> list = Collections.unmodifiableList(new ArrayList<Integer>() {{ add(2); add(3); add(5); }});
You can also initialize HashMap with values using double brace initialization as following:
Map<Integer, String> intToString = new HashMap<Integer, String>(){{ put(1, "one"); put(2, "two"); put(3, "three"); }};
This looks pretty, but it has its own set of disadvantages, which made it an anti-pattern, we'll see them in the next section.
Btw, In order to best understand design patterns, you need to work out some scenarios, examples, etc. It's best to get this kind of knowledge as part of your work but even if you don't get there, you can supplement them by joining a comprehensive course like these best Java design pattern courses and doing some object-oriented software design exercises.
Pros and Cons of Double Brace Initialization in Java
Double brace Initialization idiom internally uses the instance initializer construct in an anonymous inner class. This means it is quite obscure, and it costs an extra class every time you use it. It also holds a hidden reference to the enclosing instance, which may cause memory leaks. You cannot use the diamond operator there as well because it's not allowed to infer types in the anonymous class.Pros:
1) Less line of code
2) Creation and Initialization in the same expression
Cons:
2) Obscure, internally uses the instance initializer construct of the anonymous class.
2) It cost an extra class every time you use it
3) It holds a hidden reference to the enclosing instance, which may cause memory leaks.
Due to its disadvantage and better alternative double brace initialization is now considered as an anti-pattern in the Java world.
Alternatives of Double Brace Initialization Idiom in Java
The Good thing is that you have better alternatives to achieve the same result in Java e.g. you can create and initialize an ArrayList with values in one line by using the Copy constructor from the Collection class, as shown below :List<Integer> list = Collections.unmodifiableList( new ArrayList<>(Arrays.asList(2, 3, 5)));
Arrays.asList() returns a fixed-length list, which is passed to ArrayList copy constructor. Remember, there is difference between fixed length list returned from Arrays.asList() and the one returned from Collections.unmodifiableList().
You cannot add or remove elements from the ArrayList, but you can change the value at any index using set() in the case of former but not with the list returned by Collections.unmodifiableList() method.
This is the best method if you want a small list, but it becomes obscure and less obvious if you need Set or other Collection, since one has to create a List before creating the Set, but it's still better than double brace initialization because it doesn't create anonymous inner classes every time you use it. See Core Java for Impatient book for more details.
There is one more alternative of double brace initialization available, only if you are running in Java 8. The JDK 8 Stream API can be used to construct small collections, by combining stream factory methods and collectors, as shown below:
If you want a Set, you can use Collectors.toSet() method instead of Collectors.toList(), as seen in following example :
By the way, The streams collectors make no guarantees about the mutability of collections they return. In Java 8, the returned collections are ordinary, mutable collections such as ArrayList, HashSet, and HashMap, but this might change in future JDK releases.
That's all about the Double brace initialization idiom in Java. It's ok for one of the uses, mostly for testing and demo, but it's not good enough to be used in production code. Due to its cons, Double brace initialization has become an antipattern nowadays, especially with more suitable alternatives available.
This is the best method if you want a small list, but it becomes obscure and less obvious if you need Set or other Collection, since one has to create a List before creating the Set, but it's still better than double brace initialization because it doesn't create anonymous inner classes every time you use it. See Core Java for Impatient book for more details.
There is one more alternative of double brace initialization available, only if you are running in Java 8. The JDK 8 Stream API can be used to construct small collections, by combining stream factory methods and collectors, as shown below:
List<String> list = Collections.unmodifiableList( Stream.of("abc", "bcd", "cde").collect(toList()));
If you want a Set, you can use Collectors.toSet() method instead of Collectors.toList(), as seen in following example :
Set<String> set = Collections.unmodifiableSet( Stream.of("abc", "bcd", "cde").collect(toSet()));
By the way, The streams collectors make no guarantees about the mutability of collections they return. In Java 8, the returned collections are ordinary, mutable collections such as ArrayList, HashSet, and HashMap, but this might change in future JDK releases.
That's all about the Double brace initialization idiom in Java. It's ok for one of the uses, mostly for testing and demo, but it's not good enough to be used in production code. Due to its cons, Double brace initialization has become an antipattern nowadays, especially with more suitable alternatives available.
I still use double brace initialization for initializing static maps, but that's it. For List and Set, I prefer the combination of Arrays.asList() and copy constructor of Collection class. If I am running Java 8 then I use Stream API and Collectors to do the job.
Related Java Articles
If you like this tutorial and wants to learn more about patterns, principles, and best practices in the Java programming language, you may want to see the following articles as well:
- Why use @Override annotation in Java? (tip)
- How to write thread-safe code in Java? (tip)
- 10 Articles Every Programmer must Read. (list)
- Why should you favor Composition over Inheritance in Java? (answer)
- Some Practical tips to avoid NullPointerException in Java? (answer)
- Why getter and setter are better than public fields in Java? (answer)
- Why use SLF4j over Log4j for logging in to Java? (answer)
- 10 best practices to follow while commenting code (article)
- 10 Object-Oriented design Principle Every OOP developer should know (article)
- Why is static code analysis important for Java applications? (article)
- 10 Best Practices to follow while naming variables, class, and methods in Java? (article)
- Must know multi-threading and concurrency best practices for Java Programmers (article)
- Why you should not call System.exit() on Java Web Application? (answer)
you can use double brace init with objects as well, MyObj myObj = new MyObj() {{ setFoo(foo) }};
ReplyDeleteYes, after JDK 9, Map.of(), List.of(), and Set.of() is better than using double brace pattern.
ReplyDelete