Gilad Bracha, Generics in the Java Programming Language
1. Wildcard is a supertype of all kinds of collections.
void print Collection(Collection<?> c) { for (Object e: c) // Wildcard is an object System.out.println(e); }
2. You can not add anything into the container of ?.
Collection<?> c = new ArrayList<String>(); c.add(new Object()); // Impossible, because ? is unknown type. Null is an exception.
3. Use ? extends SuperClass for reading
public void drawAll(List<? extends Shape> shapes) { ... }
4. To add something to generic collection, use generic methods
static <T> void fromArrayToCollection(T[ ] a, Collection<T> c) { for(T o: a) c.add(o); }
Why? Because the following does not work:
static void fromArrayToCollection(Object[ ] a, Collection<?> c) { for (Object o: a) c.add(o); // No, you can't add something to the collection of ?. }
6. Generic Methods vs Wildcard
Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn’t such a dependency, use wildcard. Consider:
interface Collection<E> { // reading from c. So, it's okay to use ?. public boolean containsAll(Collection<?> c); // also for reading. public boolean addAll(Collection<? extends E> c); } interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); }
Using wildcard is short and efficient. Moreover, wildcards can be used by local variables/fields/etc, e.g., List<?> aList = …;
7. Generic classes are shared.
List<String> l1 = new ArrayList(); List<Integer> l2 = new ArrayList(); System.out.println(l1.getClass() == l2.getClass()); // prints true
8. Type variables do not exist at run time
// Unchecked warning Collections<String> cstr = (Collection<String>) new ArrayList<String>(); // Unchecked warning <T> T badCast(T t, Object o) { return (T) o; }
9. Class Literals as Run-time Type Tokens
Collection<EmpInfo< emps = sqlUtility.select(EmpInfo.class, ...); public static Collection select(Class<T> c, String sqlStatement) { ... T item = c.newInstance(); ... }
10. More fun with Wildcards
// Let me write T into Sink which accepts parents of T. public static <T> T writeAll(Collection<T> coll, Sink<? super T> snk) { ... } // Comparable TreeSet(Comparator<? super E> c) // max public static <T extends Comparable<? super T>> T max(Collection<T> coll)
Joshua Bloch, Effective Java Reloaded, JavaOne, 2006
1. Prefer Wildcards To Type Parameters
The rule: If a type variable appears only once in a method signature, use wildcard instead
The exception: conjunctive type
<E, T extends Serializable & List<E>> void f(T list) { ... }
2. Bounded wildard to the Rescue
Use extends when parameterized instance is producer(“for read”)
Use super when parameterized instance is consumer(“for write”)
public interface Shop<T> { T buy(); void sell(Collection<? extends T> lot); void buy(int numItems, Collection<? super T> myStuff); }
3. Avoid Bounded Wildcards in Return Types
They force client to deal with wildcards directly. Rarely, you do need it.
4. Typesafe Heterogeneous Container(THC) Example
public class Favorites { private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object> (); // Heterogeneous Map public <T> void setFavorite(Class<T> klass, T thing) { favorites.put(klass, thing); } public <T> T getFavorite(Class<T> klass) { return klass.cast(favorites.get(klass)); } public static void main(String[] args) { Favorites f = new Favorites(); f.setFavorite(String.class, "Java"); f.setFavorite(Integer.class, 0xcafebabe); String s = f.getFavorite(String.class); int i = f.getFavorite(Integer.class); } }
1. Type Token
Type token refers to a way to refer to the Class instance of a specific class. For example, Class<String>. This enables THC(Typesafe Heterogeneous Container). But the problem is that referring to the class of a generic class, say, List<String>.class is not possible.
2. Super Type Token
THC does not support f.setFavorite(List<String>.class, collections.emptyList()), because of Erasure. How can we do that?
Basic idea is as follows:
public abstract class TypeReference<T> {} TypeReference<List<String>> x = new TypeReference<List<String>>() {}; // Now I have type information stored somewhere
Concrete implementation of this idea:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; /** * References a generic type. * * @author crazybob@google.com (Bob Lee) */ public abstract class TypeReference<T> { private final Type type; private volatile Constructor<?> constructor; protected TypeReference() { Type superclass = getClass().getGenericSuperclass(); if (superclass instanceof Class) { throw new RuntimeException("Missing type parameter."); } this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; } /** * Instantiates a new instance of {@code T} using the default, no-arg * constructor. */ @SuppressWarnings("unchecked") public T newInstance() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (constructor == null) { Class<?> rawType = type instanceof Class<?> ? (Class<?>) type : (Class<?>) ((ParameterizedType) type).getRawType(); constructor = rawType.getConstructor(); } return (T) constructor.newInstance(); } /** * Gets the referenced type. */ public Type getType() { return this.type; } public static void main(String[] args) throws Exception { List<String> l1 = new TypeReference<ArrayList<String>>() {}.newInstance(); List l2 = new TypeReference<ArrayList>() {}.newInstance(); } }