Upper Bounds: <? extends Foo>
- Use bounded wildcard to increase API flexibility
The List<Number>
is more restrictive than List<? extends Number>
because the former matches a list of type Number only, whereas the latter matches a list of type Number or any of its subclasses
double sumOfList(List<? extends Number> list);
List<Integer> intList = new ArrayList<>();
List<? extends Integer> iList= intList;
iList.add(0); // compile error
// we can add null
iList.add(null);
Unbounded Wildcards: <?>
void printList(List<?> list)
List<Object>
andList<?>
are not the same, we can only insert null into aList<?>
.
Lower Bounded: <? super Foo>
- restricts the unknown type to be a specific type or a super type of that type
void addNumbers(List<? super Integer> list)
works onList<Integer>, List<Number>, and List<Object>
- anything that can hold Integer values.
public static void addNumbers(List<? super Integer> list) {
for (int i = 1; i <= 10; i++) {
list.add(i);
}
}
List<Integer> intList = new ArrayList<>();
List<? super Integer> olist = intList;
olist.add(0);
Unbounded Wildcards: <?>
- when any object can be used when a method can be implemented using functionality provided in the Object class or When the code is independent of the type parameter
Wildcards and Subtyping
List<Integer>
is not a subtype of List<Number>
, these two types are not related. The common parent of List<Number>
and List<Integer>
is List<?>
.
Guidelines for Wildcard Use
- An “in” variable is defined with an upper bounded wildcard, using the extends keyword.
- An “out” variable is defined with a lower bounded wildcard, using the super keyword.
- In the case where the “in” variable can be accessed using methods defined in the Object class, use an unbounded wildcard.
- In the case where the code needs to access the variable as both an “in” and an “out” variable, do not use a wildcard.
- A list defined by
List<? extends ...>
can be informally thought of as read-only
List<Integer> le = new ArrayList<>();
List<? extends Number> ln = le;
ln.add(0); // compile-time error
List<? extends Integer> ilist = new ArrayList<>();
ilist.add(0); // compile error
List<? super Integer> olist = new ArrayList<>();
olist.add(0); // work
- we can only add null to
List<? extends Integer>
Wildcard Examples
// Collections
static <T> void sort(List<T> list, Comparator<? super T> c);
static <T extends Comparable<? super T>> void sort(List<T> list);
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key);
static <T extends Comparable<? super T>> void sort(List list)
- T can implement Comparable<? super T>, not just Comparable
- It means that a Student class can implement Comparable
, where Student is a subclass of Person - It means that a Student class can implement Comparable
Type Parameter Names
- E, K, V, T, S, U, V, T1
Type Erasure
- Replace generic type parameters with their bound if bounded type parameters
are used. - Replace generic type parameters with Object if unbounded type parameters
or <?> are used. - Insert type casts to preserve type safety.
- Generate bridge methods to keep polymorphism in extended generic types.
- A type parameter cannot be used to instantiate its object inside a method.
Class<T> clazz, T instance = clazz.newInstance();
`
- static type parameters are not allowed
- no cast for generic type, except unbounded wildcards
- instanceOf doesn’t work on generic type
- No generic exception
- Can’t capture generic type exception
- Can’t have overloaded methods that can have the same signature after type erasure
- Replace generic type parameters with Object if unbounded type parameters
Generic and Array doesn’t work well
- Java Array are Covariant
- Java array checks the type of values at runtime
Object objectArray[] = new Long[1];
works.
- Generics are invariant
- we cannot assign subclass type generic to its super class generic reference because in generics any two distinct types are neither a subtype nor a supertype.
List<Object> objectList = new ArrayList<Long>();
will not compile.
- We can declare generic array, but we can’t create a generic array directly.
// compile error: Type parameter 'T' can't be instantiated directly.
T[] a= new T[10];
// compile error:
// error message from intellij can't infer arguments
// error message from javac: cannot create array with '<>', generic array creation
List<Long> array[] = new ArrayList<>[10];
- Generic and array doesn’t mix well
- Java array checks the type of values at runtime, but generic types have been erased at runtime.
- It may throw ClassCastException at runtime.
- Prefer Collection when dealing with generic types
Don’t add any more type parameters than you have to
<T> void copy(List<T> dest, List<? extends T> src) { }
// bad: unneded type
<T,V extends T> void copy(List<T> dest, List<V> src) { }
Generic in JDK Collection
- A reifiable type is a type whose type information is fully available at runtime
- Java collections have all been augmented with generics
- For backwards compatibility some methods still take an Object type.