java generics
TRANSCRIPT
Agenda
• History
• Terms
• Erasure-Reification-Subtyping
• Generification
• Generic Methods (Get and Put Principal)
• Wildcards in Detail
History
Java supports generics as of version 1.5
Before
list of integers List
list of strings List
list of lists of strings List
After
list of integers List<Integer>
list of strings List<String>
list of lists of strings List<List<String>>
«Now compiler can track what we have list of»
Terms
Term Example
Parameterized type List<String>
Actual type parameter String
Generic type List<E>
Formal type parameter E
Unbounded wildcard type List<?>
Raw type List
Bounded type parameter <E extends Number>
Recursive type bound <T extends Comparable<T>>
Bounded wildcard type List<? extends Number>
Generic method static <E> List<E> asList (E[] a)
Type token List.class
Before-after generics
// before genericsList words = new ArrayList();words.add("Hello ");words.add("world!");String s = ((String)words.get(0))+((String)words.get(1))assert s.equals("Hello world!");
// with genericsList<String> words = new ArrayList<String>();words.add("Hello ");words.add("world!");String s = words.get(0)+words.get(1); // no explicit castsassert s.equals("Hello world!");
«since generics are implemented by erasureat bytecode level, two sources above will be identical»
Use raw types in..
class literals
List.class // legal
String[].class // legal
int.class // legal
List<String>.class // illegal since erasure
List<?>.class // illegal
instanceof operator
if (o instanceof Set) {
Set<?> set = (Set<?>) o; // checked cast, no warning
}
Reification
// allocates an array that its components are type of String,
// so we say that it is reified
String[] aStringArray = new String[10];
// allocates a list with no type information,
// Java does not reify generic types
List<String> aStringList = new ArrayList<String>();
Comparing List, List<?>, List<Object>
List unboundedList = new ArrayList<Integer>();
// legal (warning)
List<?> unboundedList = new ArrayList<Integer>();
// legal, partial type-safe
List<?> unboundedList = new ArrayList<?>();
// illegal
List<Object> unboundedList = new ArrayList<Integer>();
// illegal, invariant
« List<sub> is not a subtype of List<super> »
What is the difference?static int countCommonElements(Set s1, Set s2) {
int result = 0;
for (Object o1 : s1) {
if (s2.contains( o1 ))
result++;
}
return result;
}
static int countCommonElements(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o1 : s1) {
if (s2.contains( o1 ))
result++;
}
return result;
}
Arrays« sub[] is a subtype of super[], so its covariant »
Object[] anObjectArray = new Integer[10];
// legal, covariant
anObjectArray[0] = new String("abc");
// no type safety, causes runtime exception
// like arrays, raw collection types arent’n type-safe
List list = new ArrayList();
list.add("one");
list.add(new Integer(1));
String s = (String) list.get(1); // ClassCastException
Boxing-Unboxing
public static int sum (List<Integer> ints) {
int s = 0;
for (int n : ints) { s += n; }
return s;
}
public static Integer sumInteger(List<Integer> ints) {
Integer s = 0;
for (Integer n : ints) { s += n; }
return s;
}
public class Stack {
private Object[] stack;
private int top = 0;
private static final int INITIAL_CAPACITY = 8;
public Stack() {
stack = new Object[INITIAL_CAPACITY];
}
public void push(Object obj) {
ensureCapacity();
stack[top++] = obj;
}
public Object pop() {
if (top == 0) // stack is empty
throw new EmptyStackException();
Object temp = stack[--top];
stack[top]=null;
return temp;
}
public boolean isEmpty(){
return top == 0;
}
public void ensureCapacity() {
if (stack.length == top)
stack = Arrays.copyOf(stack, 2 * top + 1);
}
}
public class GenericStack<E> {
private E[] stack;
private int top = 0;
private static final int INITIAL_CAPACITY = 8;
@SuppressWarnings( "unchecked" )
public GenericStack() {
stack = (E[]) new Object[INITIAL_CAPACITY];
}
public void push(E obj) {
ensureCapacity();
stack[top++] = obj;
}
public E pop() {
if (top == 0) // stack is empty
return null;
E temp = stack[--top];
stack[top]=null;
return temp;
}
public boolean isEmpty(){
return top == 0;
}
public void ensureCapacity() {
if (stack.length == top)
stack = Arrays.copyOf(stack, 2 * top + 1);
}
}
Generify legacy codes
Generic methods
public static <E> Set<E> union (Set<E> s1, Set<E> s2)
«Static utility methods are good candidates for generification »
formal type parameter
return type
type parameter
Get and Put Principal// PECS (producer extends, consumer super) principal
public static <T> void copy(
List<? super T> dst, List<? extends T> src ) {
for ( int i = 0; i < src.size(); i++ ) {
dst.set( i, src.get( i ) );
}
}
// usage
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints); // type inference
assert objs.toString().equals("[5, 6, four]");
Copy method (alternatives)
public static <T> void copy(
List<T> dst, List<T> src);
public static <T> void copy(
List<T> dst, List<? extends T> src);
public static <T> void copy(
List<? super T> dst, List<T> src);
public static <T> void copy(
List<? super T> dst, List<? extends T> src);
Comparables
public static <T extends Comparable<T>> T max (
List<T> list);
« All comparables and comparators are consumers »
public static <T extends Comparable<? super T>> T max( List<? extends T> list);
Tips on wildcard types
«Use wildcard types on input parameters for maximum flexibility»
«Do not use a wildcard for an input parameter if you both get and put on that
parameter»
« Do not use wildcard types as return types »
Restrictions on WildcardsInstance CreationList<?> list = new ArrayList<?>(); // illegal
List<List<?>> lists = new ArrayList<List<?>>(); // legal
lists.add(Arrays.asList(1,2,3));
lists.add(Arrays.asList("four","five"));
Generic Method CallsList<?> list = Lists.<?>factory(); // illegal
List<List<?>> list = Lists.<List<?>>factory(); // legal
Supertypesclass AnyList extends ArrayList<?> {...} // illegal
Wildcard capturing
public static <T> void reverse(List<T> list) {
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}
public static void reverse(List<?> list) {
List<Object> tmp = new ArrayList<Object>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1)); // error
}
}
Wildcard capturing (continue)
public static void reverse(List<?> list) {
rev(list);
}
private static <T> void rev(List<T> list) {
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1));
}
}
« Here we say that the type variable T has captured the wildcard. This is a generally useful technique when dealing with wildcards,
and it is worth knowing. »
Use Checked Collections to Enforce Security
private class Order { }
private class AuthenticatedOrder extends Order { }
..
List<AuthenticatedOrder> checkedList =
new ArrayList<AuthenticatedOrder>();
addChecked(Collections.checkedList(
checkedList, AuthenticatedOrder.class));
..
public void addChecked(List<AuthenticatedOrder> checkedList) {
List raw = checkedList;
Order order = new Order();
raw.add(order); // unchecked call, ClassCastException at runtime
}
References
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Angelika Langer
Effective Java
Joshua Bloch
Java Generics and Collections
Maurice Naftalin and Philip Wadler