Summary on some java generics presentations/postings

Tags:

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);
  }
}

Neal Gafter, Super Type Token

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();
    }
}