Using bounds in Java generics

adminguy's picture
Posted February 10th, 2015 by adminguy
                                                            
Using Bounds with Java Generics
 
 
Writing simple client code, like creating ArrayList objects, with generics is usually not a problem. But using generics in your own API, which will be used by others, can get pretty complex because it involves several advanced concepts like understanding bounds and using the wildcard character for providing flexibility to the client.
 
In this post we will understand how to use the wildcard character ‘ ?’ with the ‘ extends’ and ‘ super’ keywords to provide additional flexibility.
 
 
Binding a generic type with the wildcard character '?' and the 'extends' keyword
 
The Collection interface has an  addAll method, which takes as its parameter another collection, and adds all elements from the specified collection to itself (the object which it belongs to). Let’s understand some nuances of the semantics of this method.
 
If the Collection object which contains this method is a collection of Number objects (Collection<Number>) then we want to be able to add to this collection any object which is a Number, including subclasses of Number. Here’s what the method looks like:
 
 
boolean void addAll(Collection<T> t);
Code Example 1


Now we know that generics in Java do not support sub-types. What this means is, if  T refers to  Number, a client invoking this method can only give a collection of numbers. They will not be able to give it a Collection of Integer  objects (giving (Collection<Integer> ) will result in a compilation error). This restricts the capability of the addAll method to the extent of almost making it unusable. We really need a way to write the addAll method so that it can accept a Collection  of Number  and all its sub-types. But how do we do it? We need additional syntax to relax the rules in a safe way, and that is exactly what the wildcard character '?' with the extends  keyword gives us. Let’s first rewrite the method using the new syntax, after which we will also understand what it means.
 
boolean void addAll(Collection<? extends T> t);
Code Example 2
 
The syntax in Code Example 2 can be read as; if T is Number, then the object  t, can be a collection of Number objects ( Collection<Number>) or any sub-type of Number such as Integer objects (Collection<Integer>), or Double (Collection<Double> ) ... you get the point. Writing Collection<? extends T> t gave us greater flexibility as compared to Collection<T> t . Let me show you a small snippet of client code which actually adds a collection of Integer objects and another collection of Double objects to an existing collection of Number objects.
 
   
public static  void main(String args[]) {
    Collection<Number> scol = new  ArrayList<Number>();
    Collection<Double> addC1 = new ArrayList<Double>();
    Collection<Integer> addC2 = new ArrayList<Integer>();
    scol.addAll(addC1);
    scol.addAll(addC2);
}
Code example 3

If you look at the main method in Code example 3, you will notice that even though the collection ‘ scol‘ is created with the type parameter, Number , when we execute its addAll method, we are able to pass it both: a Collection of Double  objects, and a Collection  of Integer  objects. This magic was made possible by '?' and the 'extends ' keyword which essentially tells the compiler; allow any type which is a subtype of Number.
 
Now, I am sure you are thinking - doesn't this introduce the same problem that prohibiting sub-types in generics actually fixed? What the API developer writes sloppy code in the  ArrayList class’ addAll method to mutate the collection it receives as its argument. Something like this:
 
public void  addAll(Collection<?  extends T> t) {
    t.add(new  Double(  "3.33"));
}
Code Example 4
 
While this may not create a problem if ' t' were a collection of  Double objects (other than having an unwanted object in the collectionJ), it will create a huge problem for a collection of Integer objects. It will corrupt the damn collection!!! So why is this not a problem? It’s not a problem because this code won’t compile. Try writing something similar on your computer and compiling it. Here's the error message I got:
 
The method add(capture#1-of ? extends T) in the type Collection<capture#1-of ? extends T> is not applicable for the arguments (Double)
Error Message 1
 
The error message is kind of cryptic, but it does a good job of preventing us from adding a Double to a collection of Integer objects, and messing up.
 
We have just seen how we can relax the no sub-type limitation with generics with the wildcard character and the extends keyword to safely allow sub-types. In the next section, we will understand how to use the wildcard character with the super keyword.
 
 
Binding a generic type with the wildcard character '?' and the 'super' keyword
 
Similar to the 'extends' keyword, generics in Java has another keyword called ' super'. Just as we used '? extends T' to indicate a type that is a sub-type of ' T', we use ' ? super T‘ to indicate a type which is a super-type of 'T'. Let's understand this with an example. The utility class Collections, in the Java standard library, has a method called addAll  which has a signature as shown below.
 
 
public static  <T>  boolean addAll(Collection<?  super T> c, T... elements)
Code Example 5
 
 
The semantics of this method is to take all the elements specified in the varargs ' elements' and add them to the collection 'c', whose type is specified as Collection<?  super T> c. We have already seen what extends means, now let’s do some backwards reasoning to understand what super means in this context. If the type of 'elements' is 'T ' what should be the type of 'c' ? If elements of type ' T' are to be added to 'c', then 'c' should be a collection of  'T ' or a collection of one of its super-types. Let us replace 'T' with an actual type to make the concept clearer.
 
 
public static  boolean addAll(Collection<?  super Integer> c, Integer... elements)
Code Example 5
 
 
Since 'elements' is of type  Integer, ‘ c’ needs to be a collection of any type which can accept Integer objects. In plain English, we would say, ‘c ’ should be a collection of Integer or any super-type of Integer. Just as the extends keyword allows us to specify sub-types, the super keyword allows us to specify super-types. <? super Integer>  refers to Integer or any of its super-types (like Number, or even Object, which you normally would not use, but then again who are we to stop you?).

PECS
 
We have seen how to use extends and  super to make your API flexible by specifying bounds for generic type parameters. But sometimes it's easy to get confused between the two. When should we use extends and when should we use  super? There’s an acronym, PECS, to help us out.
 
PECS stands for Producer extends  Consumer super
 
Producer in this case is that object from which items are taken and consumer is that object in which items are added. If you look at Code Example 2 , you will notice that elements of Collection ' t' are added to the collection object which contains the method. This means 't ' is a producer, hence we use extends. Now take a look at Code Example 5. The objects denoted by ' elements' are added to the Collection 'c', making 'c ' the consumer, therefore we use super while defining its type.
 
 
Summary
 

In this article we saw use cases when we need to relax typing restrictions imposed by the Java generics system. We learned how to do this in a safe way with the wildcard character '?' , along with the extends and super keywords.