Generics

Generics add stability to your code by making more of your bugs detectable at compile time.

Elimination of casts

The following code snippet without generics requires casting:

List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);

When re-written to use generics, the code does not require casting:

List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0);   // no cast
public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}
Box box = new Box();
box.set("123");
Integer value = (Integer) box.get();
/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}
Box<Integer> box = new Box<Integer>();
box.set(123);
Integer value = box.get();

The Diamond

In Java SE 7 and later, you can replace the type arguments required to invoke the constructor of a generic class with an empty set of type arguments (<>) as long as the compiler can determine, or infer, the type arguments from the context. This pair of angle brackets, <>, is informally called the diamond.

Box<Integer> integerBox = new Box<>();

Multiple Type Parameters

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
    this.key = key;
    this.value = value;
    }

    public K getKey()    { return key; }
    public V getValue() { return value; }
}

The following statements create two instantiations of the OrderedPair class:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");
Pair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

Raw Types

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}
Box<Integer> intBox = new Box<>();    // parameterized type of Box<T>
Box rawBox = new Box();                // raw type of Box<T>

The @SuppressWarnings("unchecked") annotation suppresses unchecked warnings.

Generic Methods

public class Util {
    // Generic static method
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

public class Pair<K, V> {

    private K key;
    private V value;

    // Generic constructor
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    // Generic methods
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

The complete syntax for invoking this method would be:

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
boolean same = Util.compare(p1, p2);

Bounded Type Parameters

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // ...
}
NaturalNumber<Integer> n = new NaturalNumber<>(new Integer(100));

Multiple Bounds

A type parameter can have multiple bounds:

<T extends B1 & B2 & B3>

A type variable with multiple bounds is a subtype of all the types listed in the bound. If one of the bounds is a class, it must be specified first. For example:

Class A { /* ... */ }
interface B { /* ... */ }
interface C { /* ... */ }
class D <T extends A & B & C> { /* ... */ }

Generic Methods and Bounded Type Parameters

public interface Comparable<T> {
    public int compareTo(T o);
}
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
    int count = 0;
    for (T e : anArray)
        if (e.compareTo(elem) > 0)
            ++count;
    return count;
}

Target Types

static <T> List<T> emptyList();
List<String> listOne = Collections.emptyList();

It is necessary to specify the value of T in the following context:

void processStringList(List<String> stringList) {
    // process stringList
}
processStringList(Collections.<String>emptyList());
processStringList(Collections.emptyList()); // error in Java SE 7 or earlier

Upper Bounded Wildcards

To declare an upper-bounded wildcard, use the wildcard character ('?'), followed by the extends keyword, followed by its upper bound. In this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).

class MyClass {
    public static void method1(List<? extends Number> list) { /* ... */ }
    public static void method2(List<Number> list) { /* ... */ }
}
List<Integer> list = Arrays.asList(1, 2, 3);
MyClass.method1(list);
MyClass.method2(list);    // compilation error
public static double sumOfList(List<? extends Number> list) {
    double s = 0.0;
    for (Number n : list)
        s += n.doubleValue();
    return s;
}
List<Integer> li = Arrays.asList(1, 2, 3);
System.out.println("sum = " + sumOfList(li));

List<Double> ld = Arrays.asList(1.2, 2.3, 3.5);
System.out.println("sum = " + sumOfList(ld));

Unbounded Wildcards

The unbounded wildcard type is specified using the wildcard character (?), for example, List<?>. This is called a list of unknown type.

public static void printList(List<?> list) {
    for (Object elem: list)
        System.out.print(elem + " ");
    System.out.println();
}
List<Integer> li = Arrays.asList(1, 2, 3);
List<String>  ls = Arrays.asList("one", "two", "three");
printList(li);
printList(ls);

Lower Bounded Wildcards

A lower bounded wildcard restricts the unknown type to be a specific type or a super type of that type.

public static void addIntegers(List<? super Integer> list){
    list.add(new Integer(50));
}
List<Integer> integers = Arrays.asList(1, 2, 3);
addIntegers(integers);


List<Object> objects = new ArrayList<>();
addIntegers(objects);

Wildcards and Subtyping

class A { /* ... */ }
class B extends A { /* ... */ }
B b = new B();
A a = b;

This rule does not apply to generic types:

List<B> lb = new ArrayList<>();
List<A> la = lb;   // compile-time error

diagram showing that the common parent of List<Number> and List<Integer> is the list of unknown type

List<? extends Integer> intList = new ArrayList<>();
List<? extends Number>  numList = intList;  // OK. 
// List<? extends Integer> is a subtype of List<? extends Number>

The following diagram shows the relationships between several List classes declared with both upper and lower bounded wildcards.

diagram showing that List<Integer> is a subtype of both List<? extends Integer> and List<?super Integer>. List<? extends Integer> is a subtype of List<? extends Number> which is a subtype of List<?>. List<Number> is a subtype of List<? super Number> and List>? extends Number>. List<? super Number> is a subtype of List<? super Integer> which is a subtype of List<?>.

Cannot Instantiate Generic Types with Primitive Types

Pair<int, char> p = new Pair<>(8, 'a');  // compile-time error
Pair<Integer, Character> p = new Pair<>(8, 'a');

Note that the Java compiler autoboxes 8 to Integer.valueOf(8) and 'a' to Character('a'):

Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));

Cannot Create Instances of Type Parameters

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

As a workaround, you can create an object of a type parameter through reflection:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

You can invoke the append method as follows:

List<String> ls = new ArrayList<>();
append(ls, String.class);

Overloading a Method with Parameter Types

A class cannot have two overloaded methods that will have the same signature after type erasure.

public class Example {
    public void print(Set<String> strSet) { }
    public void print(Set<Integer> intSet) { }
}

The overloads would all share the same classfile representation and will generate a compile-time error.