Search code examples
javagenericscollectionsbounded-wildcard

Is there something wrong with nested ArrayList Initialization?


Consider these following classes:

class A{ }
class B extends A{ }

As we know this compiles fine:

List<? extends A> xx = new ArrayList<B>();
List<? extends List<? extends A>> xy = new ArrayList<List<? extends A>>();

But this gives compile time error

List<? extends A> yx = new ArrayList<? extends A>();
List<? extends List<? extends A>> yy = new ArrayList<? extends List<? extends A>>();

The error says:

required: class or interface without bounds

I'm aware that the fresh values interpreted by the compiler for the above initializations are different, and thus they cannot be cast safely. But what does 'without bounds' means in the above error message?


Solution

  • This error is referring to the creation of the new ArrayList whose direct, top-level type parameter is using a wildcard. This is not allowed, despite the fact that a nested type parameter is allowed to have a wildcard.

    The JLS, Section 15.9, "Class Instance Creation Expressions", states:

    If TypeArguments is present immediately after new, or immediately before (, then it is a compile-time error if any of the type arguments are wildcards (§4.5.1).

    The key word here is "immediately", because that represents the direct type argument, not the nested type argument.

    Here is the restriction mentioned in Angelika Langer's article about generics and its usages:

    They [wildcards] can not be used for creation of objects or arrays, that is, a wildcard instantiation is not permitted in a new expression. Wildcard instantiations are not types, they are placeholders for a member from a family of types. In a way, a wildcard instantiation is similar to an interface: we can declare variables of interface types, but we cannot create objects of interface types; the created objects must be of a class type that implements the interface. Similar with wildcard instantiations: we can declare variables of a wildcard instantiated type, but we cannot create objects of such a type; the created objects must be of a concrete instantiation from the family of instantiations designated by the wildcard instantiation.

    (emphasis mine)

    Basically, a wildcard type is not a concrete type and cannot be instantiated, with similar reasoning to not being allowed to create an instance of interface directly.

    However, that says nothing about nested wildcards, which relate to the creation of initially unrelated objects that may eventually be associated with this type. In your example, this would be the nested Lists that may be added to the outer ArrayList. They could be references to Lists of any matching wildcard type, but they're not being created here.

    Summary

    Java doesn't allow a wildcard in a instance creation expression (with new), but it does allow a nested wildcard in such an expression, because a direct wildcard type is not a concrete type.