Generics

Generic class vs static generic method

class A<T> {}
class B<T extends Number> {}
interface Comparable<T> {
    int compare(T t);
}
class D<T extends Comparable<T>> {}
// class Y<?> {} // doesn't compile
// class Z<? extends Number> {} //doesn't compile

class Utils {
    public static <T> T max(List<T> list) {
        return null; // impl omitted
    }
    public static <T extends Comparable<T>> T max(List<? extends T> list) {
        return null; // impl omitted
    }

    // generic static factory method
    public static <K, V> Map<K, V> createHashMap() {
        return new HashMap<K, V>();
    }
}

public class Example {
    public static void main(String[] args) {

        // explicitly specify the value of type parameter when invoking generic constructors
        A<String> a = new A<String>();
        B<Integer> b = new B<Integer>();

        // the value of type parameter is figured out by examining types of method arguments
        List<Integer> integers = Arrays.asList(1, 2, 3);
        Integer max = Utils.max(integers);
    }
}

If types T1, T2 are different,  then List<T1> is not a sub type and nor a super type of List<T2> (e.g. List<Integer> is not a sub type of List<Object>).

Even though a Circle is a Figure (Circle extends Figure), List<Circle> is NOT List<Figure> (List<Circle> doesn’t extend List<Figure>). In order to add List<Circle> , you need to change from parameter type from List<Figure> to List<? extends Figure>:

Example 1:

// List<Number> list = new ArrayList<Integer>(); // doesn't compile
List<? extends Number> list = new ArrayList<Integer>();
List<? super Number> list2 = new ArrayList<Object>();

Example 2: (PECS = Producer – Extends, Consumer – Super)

public class Example {
    static void pushAll(List<? extends Number> list) {} // here list is a source of data for the method (producer - extends)
        static void popAll(List<? super Number> list) {} // here list is a consumer, gets populated as a result of method execution (consumer - super)
        public static void main(String[] args) {
        List<Integer> ints = new ArrayList<Integer>();
        pushAll(ints);
        List<Object> objs = new ArrayList<Object>();
        popAll(objs);
   }
}

Example 3:

class Figure {}
class Circle extends Figure {}

public class Example<T> {
    public void printExactTypes(List<T> list) { }
    public void printExtendingTypes(List<? extends T> list) { }
    public static void main(String[] args) {
        Example<Figure> example = new Example<Figure>();
        List<Figure> figures = Arrays.asList(new Figure(), new Circle());
        List<Circle> circles = Arrays.asList(new Circle());
        example.printExactTypes(figures);
        // geneficClass.printExactType(integers); // doesn't compile
        example.printExtendingTypes(figures);
        example.printExtendingTypes(circles);
    }
}

Wildcards vs type parameters

Use type wildcards (<?>, <? super …>, <? extends …>)  if type parameter appears only once in method declaration. Use type parameter (<E>) if it appears in one of the cases:
– method return type and at least one method parameter
– at least two method parameters

E.g.:

public class Example {
    public static void compare(List<? extends Comparable<?>> list) {}
    public static <E extends Comparable<E>> E compare(List<? extends E> list) {
        return null; // impl omitted
    }
}

Arrays vs Lists

Arrays: runtime check, compiles but throws ArrayStoreException at runtime:

Object[] os = new Integer[1];
    os[0] = "Hello";
    System.out.println(os[0]);

Arrays: Cannot create generic array:

static <T> T[] f(T[] ts) {
    new T[1];  // compilation error
    T[] objs = (T[]) new Object[100]; // casting to generic array
    return ts;
 }

Lists: Compile time type mismatch:

List<Object> os2 = new ArrayList<Long>();

It is not recommended to use wildcards as return types.

public class Example {
    public static <T> List<T> merge(List<T> list1, List<T> list2) {
        return list2; // impl omitted
    }
    public static <T> List<? extends T> merge2(List<? extends T> list1, List<? extends T> list2) {
        return list2; // impl omitted
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> merge3(List<? extends T> list1, List<? extends T> list2) {
        return (List<T>) list2; // impl omitted
    }

    public static void main(String[] args) {
        List<Car> cars = Arrays.asList(new Car(), new Toyota());
        List<Toyota> toyotas = Arrays.asList(new Toyota(), new Toyota());

        // List<Car> cars2 = toyotas; // compilation failure
 
        // merge(cars, toyotas); // compilation failure

        List<? extends Car> merge2 = merge2(cars, toyotas);

        List<Car> merge3 = merge3(cars, toyotas);

        // List<Car> merge33 = merge3(toyotas, toyotas); // compilation failure

        List<Car> merge333 = Example.<Car> merge3(toyotas, toyotas);
 
    }
}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s