Generics add stability to your code by making more of your bugs detectable at compile time.
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();
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<>();
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>(...));
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.
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);
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));
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> { /* ... */ }
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;
}
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
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));
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);
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);
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
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.
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'));
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);
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.