Section 2.6. Wildcards Versus Type Parameters


2.6. Wildcards Versus Type Parameters

The contains method checks whether a collection contains a given object, and its generalization, containsAll, checks whether a collection contains every element of another collection. This section presents two alternate approaches to giving generic signatures for these methods. The first approach uses wildcards and is the one used in the Java Collections Framework. The second approach uses type parameters and is often a more appropriate alternative.

Wildcards Here are the types that the methods have in Java with generics:

 interface Collection<E> {   ...   public boolean contains(Object o);   public boolean containsAll(Collection<?> c);   ... } 

The first method does not use generics at all! The second method is our first sight of an important abbreviation. The type Collection<?> stands for:

 Collection<? extends Object> 

Extending Object is one of the most common uses of wildcards, so it makes sense to provide a short form for writing it.

These methods let us test for membership and containment:

 Object obj = "one"; List<Object> objs = Arrays.<Object>asList("one", 2, 3.14, 4); List<Integer> ints = Arrays.asList(2, 4); assert objs.contains(obj); assert objs.containsAll(ints); assert !ints.contains(obj); assert !ints.containsAll(objs); 

The given list of objects contains both the string "one" and the given list of integers, but the given list of integers does not contain the string "one", nor does it contain the given list of objects.

The tests ints.contains(obj) and ints.containsAll(objs) might seem silly. Of course, a list of integers won't contain an arbitrary object, such as the string "one". But it is permitted because sometimes such tests might succeed:

 Object obj = 1; List<Object> objs = Arrays.<Object>asList(1, 3); List<Integer> ints = Arrays.asList(1, 2, 3, 4); assert ints.contains(obj); assert ints.containsAll(objs); 

In this case, the object may be contained in the list of integers because it happens to be an integer, and the list of objects may be contained within the list of integers because every object in the list happens to be an integer.

Type Parameters You might reasonably choose an alternative design for collectionsa design in which you can only test containment for subtypes of the element type:

 interface MyCollection<E> {  // alternative design   ...   public boolean contains(E o);   public boolean containsAll(Collection<? extends E> c);   ... } 

Say we have a class MyList that implements MyCollection. Now the tests are legal only one way around:

 Object obj = "one"; MyList<Object> objs = MyList.<Object>asList("one", 2, 3.14, 4); MyList<Integer> ints = MyList.asList(2, 4); assert objs.contains(obj); assert objs.containsAll(ints) assert !ints.contains(obj);       // compile-time error assert !ints.containsAll(objs);   // compile-time error 

The last two tests are illegal, because the type declarations require that we can only test whether a list contains an element of a subtype of that list. So we can check whether a list of objects contains a list of integers, but not the other way around.

Which of the two styles is better is a matter of taste. The first permits more tests, and the second catches more errors at compile time (while also ruling out some sensible tests). The designers of the Java libraries chose the first, more liberal, alternative, because someone using the Collections Framework before generics might well have written a test such as ints.containsAll(objs), and that person would want that test to remain valid after generics were added to Java. However, when designing a new generic library, such as MyCollection, when backward compatibility is less important, the design that catches more errors at compile time might make more sense.

Arguably, the library designers made the wrong choice. Only rarely will a test such as ints.containsAll(objs) be required, and such a test can still be permitted by declaring ints to have type List<Object> rather than type List<Integer>. It might have been better to catch more errors in the common case rather than to permit more-precise typing in an uncommon case.

The same design choice applies to other methods that contain Object or Collection<?> in their signature, such as remove, removeAll, and retainAll.




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