4

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?

Radiodef
  • 37,180
  • 14
  • 90
  • 125
mark42inbound
  • 364
  • 1
  • 4
  • 19

3 Answers3

4

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.

Community
  • 1
  • 1
rgettman
  • 176,041
  • 30
  • 275
  • 357
2

The type arguments to the class instance being created can't be wildcards (§15.9):

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

That's pretty much it. The type arguments may contain wildcards (as in new ArrayList<List<?>>), but wildcards can't be given directly (as in new ArrayList<?>).

This distinction is maybe clarified by the grammar of type arguments which the above quote references (§4.5.1):

TypeArguments:
    < TypeArgumentList >

TypeArgumentList:
    TypeArgument {, TypeArgument}

TypeArgument:
    ReferenceType
    Wildcard

Wildcard:
    {Annotation} ? [WildcardBounds]

WildcardBounds:
    extends ReferenceType
    super ReferenceType

In other words, for each TypeArgument in the TypeArgumentList provided to the class instance creation expression, the TypeArgument may only be a ReferenceType and not a Wildcard. If a reference type is itself generic, then it can have type arguments provided to it which are wildcards.

Radiodef
  • 37,180
  • 14
  • 90
  • 125
0

At the time of construction / defenition of a generic type the type has to be specified. Both of these

? extends A
? extends List<? exetnds A>

are creating a new wildcard which is an yet unknown generic type (you can tell from the ? in front).

Both of these

B
List<? extends A>

are actual types. So when calling

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

The generic type is specified. In once case it is B, in the other it is a List containing ? extends A.

When calling

new ArrayList(); new ArrayList>();

Both times the generic type is undefined. This is not allowed.

The reason why List<? extends A> doesn't cause the error is, because here it is a type. The List within your original List<? extends List<? extends A>> wasn't created yet. You only define that later it has to be a List<? extends A>. If you would try to create this list like this

new ArrayList<? extends A>();

You will run into the same problem.

Understandable?

Basti
  • 1,117
  • 12
  • 32