Showing posts with label boundedness. Show all posts
Showing posts with label boundedness. Show all posts

Tuesday, 19 April 2011

The Get and Put Principle in bounded wildcard (Generics)

Use extends only when you intend to get values out of a structure or Collection, use super only when you intend to put values into a structure or Collection.
This also implies: don’t use any wildcards when you intend to both get and put values into and out of a structure.

// Copy all elements, subclasses of T, from source to dest 
//which contains elements that are superclasses of T.public static <T> void copy(List<? super T> dest, List<? extends T> source) {
for (int i = 0; i < source.size(); i++) {
dest.set(i, source.get(i));
}
}

// Extends wildcard violationList<Integer> integers = new LinkedList<Integer>();
List<? extends Number> numbers = integers;
numbers.get(i); // Works fine!numbers.add(3); // Won't compile!
// Super wildcard violationList<Number> numbers = new LinkedList<Number>();
List<? super Integer> integers = numbers;
numbers.add(3); // Works fine!int i = numbers.get(0); // Won't' compile!Object o = numbers.get(0); // Works fine since object is the upper bound!

Restrictions on wildcards in generics:

The get and put principle is one of the restrictions on wildcards. Additional to the above principle there are also a few restrictions on wildcards: Don’t use wilcards when creating instances, specifying generic method calls and extending superclasses:
List<?> integers = new LinkedList<?>();         // Won't compile!List<?> integers = Lists.<?>factory();          // Won't compile!class AnyList implements List<?> {}             // Won't compile!

Using multiple bounds in generics

The <A extends B> syntax applies even if B is an interface because 1) it doesn't matter operationally if B is a class or an interface and 2) if B is a type parameter, the compiler doesn't know a priori whether it is a class or an interface.  
Since Java allows multiple inheritance in the form of implementing multiple interfaces, it is possible that multiple bounds are necessary to specify a type parameter.    To express this situation, one uses the following syntax:
<T extends A & B & C & ...>
For instance:
  interface A {
...
}

interface B {
...
}

class MultiBounds<T extends A & B> {
...
}

Lower Bounded Wildcards in Generics

Suppose we wish to write a method called copyTo()such that it copies the data the opposite direction, i.e. from the host object to the given object:
public void copyTo(Box<E> b) {
  b.data = this.data(); 
}

The above code works fine so long as you are copying from one Box to another of exactly the same type (invariant type relationship), e.g. both are Box<Integer> or both are Box<String>. 

But operationally, b could be a Box of any type that is a superclass of E and the copying would be type safe.  This is a contravariant type relationship between Box<E> amd the input parameter to the copyTo() method where the type parameter of the object referenced by the variable b  is a superclass of E.
To express this, we write
public void copyTo(Box<? super E> b) {
  b.data = this.data();  // b.data is a superclass of this.data
}

The type parameterization <? super E> is called a "lower bounded wildcard" because it defines a type that could be any type so long as it is bounded by the subclass E.
Once again, we have greatly increased the utility of the copyTo() method because it is now not limited to only inputs of type Box<E> but will now work with any compatible Box object.
Upper and lower bounded wildcards will prove indispensable when implementing the visitor pattern on generic (parameterized) classes.