Instance initializer (double brace)

It is possible to use a double brace initializer for the collection initialization. It fits especially well when the collection is the value of an instance field, so it is initialized automatically during object creation. Here is an example:

public class ManageCollections {
private List<String> list = new ArrayList<>() {
{
add(null);
add("s2");
add("s3");
}
};
public List<String> getThatList(){
return this.list;
}
public static void main(String... args){
ManageCollections mc = new ManageCollections();
System.out.println(mc.getThatList()); //prints: [null, s2, s3]
}
}

We have added a getter and use it when the main() method runs. Unfortunately, the double brace initializer does not save any time typing compared with the traditional collection initialization in a constructor:

public class ManageCollections {
private List<String> list = new ArrayList<>();
public ManageCollections(){
list.add(null);
list.add("s2");
list.add("s3");
}
public List<String> getThatList(){
return this.list;
}
public static void main(String... args){
ManageCollections mc = new ManageCollections();
System.out.println(mc.getThatList()); //prints: [null, s2, s3]
}
}

The only difference is that you need to type the list variable for each call of add() method. Besides, the double brace initializer has an overhead of creating an anonymous class with just an instance initializer and references to the enclosing class. It also has potentially more problems, so should be avoided.

The good news is that there is a much shorter and more convenient way to initialize a collection as the field value or as a local variable value:

private List<String> list = Arrays.asList(null, "s2", "s3");

The static method asList() of the java.util.Arrays class is very popular (we will talk about the Arrays class in more detail shortly). The only potential drawback is that such a list does not allow the addition of elements:

List<String> list = Arrays.asList(null, "s2", "s3");
list.add("s4"); // throws UnsupportedOperationException

But, we can always create a new collection by passing the initialized list into a constructor:

List<String> list = new ArrayList(Arrays.asList(null, "s2", "s3"));
list.add("s4"); //works just fine

Set<String> set = new HashSet<>(Arrays.asList(null, "s2", "s3"));
set.add("s4"); //works just fine as well

Notice that the constructors of collection classes accept any object that implements the Collection interface. It allows lists to be created from sets, and vice versa. But, the Map interface does not extend Collection, so Map implementations only allow a map to be created from another map:

Map<Integer, String> map = new HashMap<>();
map.put(1, null);
map.put(2, "s2");
map.put(3, "s3");

Map<Integer, String> anotherMap = new HashMap<>(map);

The types of keys and values of the new map have to be either the same as in the provided map or have to be parents of the types of the provided map:

class A{}
class B extends A{}
Map<Integer, B> mb = new HashMap<>();
Map<Integer, A> ma = new HashMap<>(mb);

For example, this is an acceptable assignment:

Map<Integer, String> map1 = new HashMap<>();
Map<Integer, Object> map2 = new HashMap<>(map1);

That is because the HashMap constructor limits the types just to the children of the map elements:

HashMap(Map<? extends K,? extends V> map)

There's a similar problem with the following code too:

class A {}
class B extends A {}
List<A> l1 = Arrays.asList(new B());
List<B> l2 = Arrays.asList(new B());
//List<B> l3 = Arrays.asList(new A()); //compiler error

The preceding code makes sense, doesn't it? class B has (inherits) all the non-private methods and fields of class A, but can have other non-private methods and fields that are not available in class A. Even if both classes are empty today, as in our example, tomorrow we may decide to add some methods to class B. So, the compiler protects us from such a case and does not allow a collection with elements of a parent type to be assigned to the collection of children. And that is the meaning of the generics in the following constructor definitions, as you see them in the Java Standard Library API's java.util package:

ArrayList(Collection<? extends E> collection)
HashSet(Collection<? extends E> collection)
HashMap(Map<? extends K,? extends V> map)

We hope that by now, you have become more comfortable with such generics. If in doubt, read the sections about generics in the previous chapter.