Section 2.8. Restrictions on Wildcards


2.8. Restrictions on Wildcards

Wildcards may not appear at the top level in class instance creation expressions (new), in explicit type parameters in generic method calls, or in supertypes (extends and implements).

Instance Creation In a class instance creation expression, if the type is a parameterized type, then none of the type parameters may be wildcards. For example, the following are illegal:

 List<?> list = new ArrayList<?>();  // compile-time error Map<String, ? extends Number> map   = new HashMap<String, ? extends Number>();  // compile-time error 

This is usually not a hardship. The Get and Put Principle tells us that if a structure contains a wildcard, we should only get values out of it (if it is an extends wildcard) or only put values into it (if it is a super wildcard). For a structure to be useful, we must do both. Therefore, we usually create a structure at a precise type, even if we use wildcard types to put values into or get values from the structure, as in the following example:

 List<Number> nums = new ArrayList<Number>(); List<? super Number> sink = nums; List<? extends Number> source = nums; for (int i=0; i<10; i++) sink.add(i); double sum=0; for (Number num : source) sum+=num.doubleValue(); 

Here wildcards appear in the second and third lines, but not in the first line that creates the list.

Only top-level parameters in instance creation are prohibited from containing wildcards. Nested wildcards are permitted. Hence, the following is legal:

 List<List<?>> lists = new ArrayList<List<?>>(); lists.add(Arrays.asList(1,2,3)); lists.add(Arrays.asList("four","five")); assert lists.toString().equals("[[1, 2, 3], [four, five]]"); 

Even though the list of lists is created at a wildcard type, each individual list within it has a specific type: the first is a list of integers and the second is a list of strings. The wildcard type prohibits us from extracting elements from the inner lists at any type other than Object, but since that is the type used by toString, this code is well typed.

One way to remember the restriction is that the relationship between wildcards and ordinary types is similar to the relationship between interfaces and classeswildcards and interfaces are more general, ordinary types and classes are more specific, and instance creation requires the more specific information. Consider the following three statements:

 List<?> list = new ArrayList<Object>();   // ok List<?> list = new List<Object>()  // compile-time error List<?> list = new ArrayList<?>()  // compile-time error 

The first is legal; the second is illegal because an instance creation expression requires a class, not an interface; and the third is illegal because an instance creation expression requires an ordinary type, not a wildcard.

You might wonder why this restriction is necessary. The Java designers had in mind that every wildcard type is shorthand for some ordinary type, so they believed that ultimately every object should be created with an ordinary type. It is not clear whether this restriction is necessary, but it is unlikely to be a problem. (We tried hard to contrive a situation in which it was a problem, and we failed!)

Generic Method Calls If a generic method call includes explicit type parameters, those type parameters must not be wildcards. For example, say we have the following generic method:

 class Lists {   public static <T> List<T> factory() { return new ArrayList<T>(); } } 

You may choose for the type parameters to be inferred, or you may pass an explicit type parameter. Both of the following are legal:

 List<?> list = Lists.factory(); List<?> list = Lists.<Object>factory(); 

If an explicit type parameter is passed, it must not be a wildcard:

 List<?> list = Lists.<?>factory();  // compile-time error 

As before, nested wildcards are permitted:

 List<List<?>> = Lists.<List<?>>factory();  // ok 

The motivation for this restriction is similar to the previous one. Again, it is not clear whether it is necessary, but it is unlikely to be a problem.

Supertypes When a class instance is created, it invokes the initializer for its supertype. Hence, any restriction that applies to instance creation must also apply to supertypes. In a class declaration, if the supertype or any superinterface has type parameters, these types must not be wildcards.

For example, this declaration is illegal:

 class AnyList extends ArrayList<?> {...} // compile-time error 

And so is this:

 class AnotherList implements List<?> {...} // compile-time error 

But, as before, nested wildcards are permitted:

 class NestedList implements ArrayList<List<?>> {...}  // ok 

The motivation for this restriction is similar to the previous two. As before, it is not clear whether it is necessary, but it is unlikely to be a problem.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net