Develop code that makes proper use of type parameters in class/interface declarations, instance variables, method arguments, and return types; and write generic methods or methods that make use of wildcard types and understand the similarities and differences between these two approaches.

Parameters and arguments

In Java 5.0 code, generics will manifest itself in two forms, as type parameters and as type arguments. You will be familiar with the distinction between parameters and arguments in methods:

void foo(int aaa, int bbb) {
	...
}

void bar(int ccc) {
	foo(ccc, 142);
}
					
					
Above, aaa and bbb are the parameters to foo(). When foo() is called, ccc and 142 are passed as arguments. Parameters are the "generic" part, and arguments are the "specific" part.

Also note that ccc is both a parameter of bar(...) and an argument to foo(...) (this will also happen in generics with type parameters and arguments).

Reading generics is a matter of working out where a type parameter is being declared, and where a type argument is being passed.

Type parameters

Type parameters can appear in two locations, class (or interface) declarations, and method declarations. When type parameters are used, we are saying that this class/interface/method body is parameterized over those types. We can use those type parameters in the body as if they were a real classname we had imported, but we don't care what actual class it is.

A generic class/interface is declared by putting type parameters after the name of the class/interface. Type parameters begin and end with angle brackets and are separated by commas. You can specify more than one type parameter, and each type parameter can have a bound (constraint):

					
public class Foo <TypeParam1, TypeParam2> {
	...
	// type parameters used here
	...
}

					
or
					
public interface I<T> {
	public T getData();
	public void releaseData(T data);
}

					

A type bound places a constraint on the type arguments that can be passed to the type parameter.

No bound, any type argument can be used:

					
<T>

					

T can be any subclass of InputStream:

					
<T extends InputStream>

					

T must implement the Serializable interface (NOTE, that extends is used here, even when talking about implementing interfaces):

					
<T extends Serializable>

					

T must be a subclass of InputStream and implement Serializable:

					
<T extends InputStream & Serializable>

					

T must implement three interfaces:


<T extends Serializable & Runnable & Cloneable>

					

Two type parameters (such as the type of the keys, and type of the values, in a Map):


<K, V>

					

Two type parameters; the second bound is defined in terms of the first. Type bounds can be mutually- or even self-recursive:


<T, C extends Comparator<T>> 

					

Lets define our own immutable "pair" class. Notice how we have declared fields and methods in terms of the type parameters. getFirst() is a method in a generic class and uses one of the generic type parameters, but that is different to a generic method:


public class Pair <X, Y> {
	private final X a;
	private final Y b;
    
	public Pair(X a, Y b) {
		this.a = a;
		this.b = b;
	}
   
	public X getFirst() {
		return a;
	}
	public Y getSecond() {
		return b;
	}
}
					
					

Methods can have their own type parameters, independent of the type parameters of the enclosing class (or even if the enclosing class is not generic). The type parameters go just before the return type of the method declaration:


class PairUtil {
	
	public static <A extends Number, B extends Number> double add(Pair<A, B> p) {
		return p.getFirst().doubleValue() + p.getSecond().doubleValue();
	}

	public static <A, B> Pair<B, A> swap(Pair<A, B> p) {
		A first = p.getFirst();
		B second = p.getSecond();
		return new Pair<B, A>(second, first);
	}
}
					
					

We have done a few things in the add(...) method:

The second method, swap(...), is even more interesting:

Type arguments

Type parameters are for defining generic classes; type arguments are for using generic classes. And you will be using generic classes far more often than you write them.

Wherever you use a generic classname, you need to supply the appropriate type arguments. These arguments go straight after the classname, surrounded by angle brackets. The arguments you supply must satisfy any type bounds on the type parameters. There are 5 main contexts where you can use type arguments, shown here.

  1. Variable declaration.

    When using a generic class for a variable (local, field or method parameter), you need to supply the type arguments:

    
    Pair<String, Integer> p1;
    Pair<Integer, String> p2;
    Pair<String, Integer> p3;
    
    								

  2. Constructor call.

    When calling the constructor of a generic class, the type arguments must also follow the classname (that is, the constructor name).

    
    String s = "foo";
    Integer i = new Integer(3);
    p1 = new Pair<String, Integer>(s, i);
    
    								

  3. Inferred generic-method call.

    When calling a generic method, the method's arguments may contain enough information for the compiler to infer the type arguments to the method call.

    
    ...
    public static <A, B> Pair<B, A> swap(Pair<A, B> p) {
    ...
    
    p2 = PairUtil.swap(p1);                 
    
    								

  4. Explicit generic-method call.

    When calling a generic method where you do need to supply the type arguments, the arguments go after the dot of the method call. This applies to static or non-static method calls.

    
    ...
    public static <A, B> Pair<B, A> swap(Pair<A, B> p) {
    ...
    
    p2 = PairUtil.<String,Integer>swap(p1);
    
    								

  5. Casting.

    You can cast to a generic class, and you can supply the type arguments. But you get a compiler warning for that:

    
    Object o = p1;
    p3 = (Pair<String, Integer>) o; // WARNING !!!
    
    								

Declaring instance variables


public class Basket<E> {
	...
}

class Fruit {
}

class Apple extends Fruit {
}

class Orange extends Fruit {
}
					
					

					
Basket b = new Basket(); // OK !
Basket b1 = new Basket<Fruit>();  // OK !
Basket<Fruit> b2 = new Basket<Fruit>(); // OK !

// Type mismatch: cannot convert from Basket<Fruit> to Basket<Apple>
Basket<Apple> b3 = new Basket<Fruit>(); // WRONG !!! 

// Type mismatch: cannot convert from Basket<Apple> to Basket<Fruit>
Basket<Fruit> b4 = new Basket<Apple>();  // WRONG !!!

Basket<?> b5 = new Basket<Apple>(); // OK !

// 1. Cannot instantiate the type Basket<?>
// 2. Type mismatch: cannot convert from Basket<?> to Basket<Apple>
Basket<Apple> b6 = new Basket<?>(); // WRONG !!!

					

Implementing generic types

In addition to using generic types, you can implement your own. A generic type has one or more type parameters. Here is an example with only one type parameter called E. A parameterized type must be a reference type, and therefore primitive types are NOT allowed to be parameterized types.

					
interface List<E> {
	void add(E x);
	Iterator<E> iterator();
}

interface Iterator<E> {
	E next();
	boolean hasNext();
}


class LinkedList<E> implements List<E> {
	// implementation   
}

					

Here, E represents the type of elements contained in the collection. Think of E as a placeholder that will be replaced by a concrete type. For example, if you write LinkedList<String> then E will be replaced by String.

In some of your code you may need to invoke methods of the element type, such as Object's hashCode() and equals(). Here is an example that takes two type parameters:

					
class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
	...
	public V get(Object k) {
		...
		int hash = k.hashCode();  
		...
	}
}

					
The important thing to note is that you are required to replace the type variables K and V by concrete types that are subtypes of Object.

Generic methods

Genericity is not limited to classes and interfaces, you can define generic methods. Static methods, nonstatic methods, and constructors can all be parameterized in almost the same way as for classes and interfaces, but the syntax is a bit different. Generic methods are also invoked in the same way as non-generic methods.

Before we see an example of a generics method, consider the following segment of code that prints out all the elements in a collection:

public void printCollection(Collection c) {
	Iterator i = c.iterator();
	for(int k = 0; k < c.size() ; k++) {
		out.printn(i.next());
	}
}
					

Using generics, this can be re-written as follows. Note that the Collection<?> is the collection of an unknown type:


void printCollection(Collection<?> c) {
	for(Object o:c) {
		out.println(e);
	}
}

					
This example uses a feature of generics known as wildcards.

Some more generics examples:


public class Basket<E> {
	private E element;
	
	public void setElement(E x) {
		element = x;
	}
	
	public E getElement() {
		return element;		
	}
}

class Fruit {
}

class Apple extends Fruit {
}

class Orange extends Fruit {
}
						
					
A client code (compilation problem):

Basket<Fruit> basket = new Basket<Fruit>();
basket.setElement(new Apple()); // OK, can assign Apple reference to Fruit variable

// Type mismatch: cannot convert from Fruit to Apple !!!
Apple apple = basket.getElement(); // WRONG ! Compilation error ! 

Apple apple = (Apple) basket.getElement(); // OK ! Compiles and runs fine.

					

A client code (runtime exception):


Basket<Fruit> basket = new Basket<Fruit>();
basket.setElement(new Apple());
Orange orange = (Orange) basket.getElement(); // Runtime exception !!!
					
					
Exception in thread "main" java.lang.ClassCastException: Apple
	at Client.main(Client1.java:8)
					

Wildcards

There are three types of wildcards:

  1. "? extends Type": Denotes a family of subtypes of type Type. This is the most useful wildcard.

  2. "? super Type": Denotes a family of supertypes of type Type.

  3. "?": Denotes the set of all types or ANY.

As an example of using wildcards, consider a draw() method that should be capable of drawing any shape such as circle, rectangle, and triangle. The implementation may look something like this. Here Shape is an abstract class with three subclasses: Circle, Rectangle, and Triangle:


public void draw(List<Shape> shape) {
	for(Shape s: shape) {
		s.draw(this);
	}
}

					

It is worth noting that the draw(...) method can only be called on lists of Shape and cannot be called on a list of Circle, Rectangle, and Triangle for example. In order to have the method accept any kind of shape, it should be written as follows:


public void draw(List<? extends Shape> shape) {
	for(Shape s: shape) {
		s.draw(this);
	}
}

					
					
					


public static <T extends Comparable<? super T>> void sort(List<T> list) {
	Object a[] = list.toArray();
	Arrays.sort(a);
	ListIterator<T> i = list.listIterator();
	for (int j = 0; j < a.length; j++) {
		...
	}
}

					

Another example of unbounded wildcards:


Basket<?> basket = new Basket<Apple>();
basket.setElement(new Apple()); // WRONG !!!
Apple apple = (Apple) basket.getElement();
					
					
The compiler does not know the type of the element stored in basket. That is why it cannot guarantee an apple can be inserted into the basket basket. So the statement basket.setElement(new Apple()) is not allowed. The methode basket.setElement(...) cannot be used at all (read only collection).

If we have some subclasses of the Apple class:


class GoldenDelicious extends Apple {}
class Jonagold extends Apple {}
					
					
And want to create a parameterized method which will accept basket of any apples, then we can create the following method with wildcards:
					
public static boolean isRipeInBasket(Basket<? extends Apple> basket) {
	Apple apple = basket.getElement();
	...
}	

					
or parameterized method:

public static <A extends Apple> boolean isRipeInBasket(Basket<A> basket) {
	Apple apple = basket.getElement();
	...
}

					

If we want to add some sort of apples to basket, the following generic method is required:


public static <A extends Apple> void insert(A apple, Basket<? super A> basket) {
	basket.setElement(apple);
}
					
					
or

public static <A extends Apple> void insert(Apple apple, Basket<? super A> basket) {
	basket.setElement(apple);
}

					

Wild-cards in type arguments

Java 5.0 allows the use of a wild-card in a type argument, in order to simplify the use of generics (easier to type, and to read).

For example, you may want to use a generic class, but you don't particularly care what the type argument is. What you could do is specify a "dummy" type parameter T, as in the generic method count_0() below:


public static <T> int count_0(List<T> list) {
    int count = 0;
    for (T n : list) {
        count++;
    }
    return count;
}
					
					

Alternatively, you can avoid creating a generic method, and just use a ? wild-card as in count_1(). You should read List<?> as "a list of whatever":


public static int count_1(List<?> list) {
    int count = 0;
    for (Object n : list) {
        count++;
    }
    return count;
}
					
					

Wild-cards with upper and lower bounds

The next two methods use a bounded wild-card to express the required subclass/superclass relationship. You should read List<? extends T> as "A list of T, or any subclass of T". You can read List<? super T> as "A list of T, or any T's super-classes":


List<Number> listOfNumbers = new ArrayList<Number>();
listOfNumbers.add(new Integer(3));
listOfNumbers.add(new Double(4.0));

List<Integer> listOfIntegers = new ArrayList<Integer>();
listOfIntegers.add(new Integer(3));
listOfIntegers.add(new Integer(4));		

		
addAll_1(listOfIntegers, listOfNumbers);
addAll_2(listOfIntegers, listOfNumbers);
...

/**
 * Append src to dest, so long as "whatever the types of things in src are",
 * they extend "the types of things in dest".
 */
public static <T> void addAll_1(List<? extends T> src, List<T> dest) {
	for (T o : src) {
		dest.add(o);
	}
}

/**
 * Append src to dest, so long as "whatever the types of things dest can hold",
 * they are a superclass of "the types of things in src".
 */
public static <T> void addAll_2(List<T> src, List<? super T> dest) {
	for (T o : src) {
		dest.add(o);
	}
}
					
					

Erasure

Generics are implemented by the Java compiler as a front-end conversion called erasure, which is the process of translating or rewriting code that uses generics into non-generic code (that is, maps the new syntax to the current JVM specification). In other words, this conversion erases all generic type information; all information between angle brackets is erased. For example, LinkedList<Integer> will become LinkedList. Uses of other type variables are replaced by the upper bound of the type variable (for example, Object), and when the resulting code is not type correct, a cast to the appropriate type is inserted.

Let's take a look at the following code:


public class Basket<E> {
	private E element;
	
	public void setElement(E x) {
		element = x;
	}
	
	public E getElement() {
		return element;		
	}
}

class Fruit {
}

class Apple extends Fruit {
}

class Orange extends Fruit {
}
					
					
The following code will compile and run correctly:

Basket basket = new Basket<Orange>();
basket.setElement(new Apple());
Apple apple = (Apple) basket.getElement();
					
					
Beause we use a generic class without specifying the type of its element, the compiled code is not type safe and the compiler will issue a warning. During the runtime the JVM has no information about the types of the elements of the used Basket and so it cannot tell a difference between a Basket<Orange> and a Basket<Apple>. So we are indeed allowed to insert an apple where only oranges should be allowed (we have been warned). Since there is an Apple in the basket, no exception will be thrown in Apple apple = (Apple)basket.getElement().

The following example gives compile time error:


public <A extends Fruit> void erasureTest(A a) {}
public <B extends Fruit> void erasureTest(B b) {} // WRONG !!!
					
					
Both of them will look like this at runtime:
public void erasureTest(Fruit a) {}
public void erasureTest(Fruit b) {} // WRONG !!!
					

The following methods also will cause a compile time error:


public static void erasureTest2(Basket<? extends Apple> basket) {}	
public static void erasureTest2(Basket<? extends Orange> basket) {} // WRONG !!!
					
					
At the runtime signatures of these 2 methods will be:
public static void erasureTest2(Basket basket) {}	
public static void erasureTest2(Basket basket) {} // WRONG !!!
					

The following code is a legal example of overloaded methods:


public static <A extends Apple>	 void erasureTest2(A a, Basket<? super A> b){} // OK
public static <G extends Orange> void erasureTest2(G g, Basket<? super G> b){} // OK
					
					
The compiler converts the signatures of the insertRipe methods to the following ones:
public static void erasureTest2(Apple a,  Basket b)		
public static void erasureTest2(Orange g, Basket b)
Those signatures are different and so the method name can be overloaded.

The following code will compile (with warnings):


Basket<Orange> bO = new Basket<Orange>();
Basket b = bO;
Basket<Apple> bA = (Basket<Apple>) b; // WARNING
					
					
Because at runtime the last line will be:
Basket bA = (Basket) b;
					

The using of instanceof is ILLEGAL with parameterized types:


Collection cs = new ArrayList<String>();
if (cs instanceof Collection<String>) { // WRONG !!! Compilation error !
	...
}
					
					

Arrays and generics

The component type of an array object may not be a type variable or a parameterized type, unless it is an (unbounded) wildcard type. You can declare array types whose element type is a type variable or a parameterized type, but not array objects.


// Cannot create a generic array of Basket<Apple>
Basket<Apple>[] b  = new Basket<Apple>[10]; // WRONG !!!!

// Cannot create a generic array of Basket<Apple>
Basket<?>[] b1 = new Basket<Apple>[10]; // WRONG !!!

Basket<?>[] b2 = new Basket<?>[10]; // OK !

public <T> T[] test() { // OK !
	return null;
}

// Cannot create a generic array of T
public <T> T[] test1() { // WRONG !!!
	return new T[10];
} 
					
					

Professional hosting     Belorussian informational portal         Free SCWCD 1.4 Study Guide     Free SCBCD 1.3 Study Guide     Free SCDJWS 1.4 Study Guide     SCDJWS 1.4 Quiz     Free IBM Certified Associate Developer Study Guide     Free Mock Exam Engine     IBM Test 000-287. Enterprise Application Development with IBM WebSphere Studio, V5.0 Study Guide