Search code examples
javagenericswildcardbounded-wildcard

Generic lower unbound vs upper bounded wildcards


import java.util.List;
import java.util.ArrayList;

interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
    public static void main(String[] args){
        List<Dog> d = new ArrayList<Dog>();
        List<Collie> c = new ArrayList<Collie>();
        d.add(new Collie());
        c.add(new Collie());
        do1(d); do1(c);
        do2(d); do2(c);
    }
    static void do1(List<? extends Dog> d2){
        d2.add(new Collie());
        System.out.print(d2.size());
    }
    static void do2(List<? super Collie> c2){
        c2.add(new Collie());
        System.out.print(c2.size());
    }
}

The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)

What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?

As far as I know, The method do1 has List<? extends Dog> d2 so d2 only can be accessed but not modified. The method d2 has List<? super Collie> c2 so c2 can be accessed and modified and there is no compilation error.

Generic guidelines


Solution

  • The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)

    That's a fair first approximation, but not quite correct. More correct would be:

    You can only add null to a Collection<? extends Dog> because its add method takes an argument of ? extends Dog. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog, the compiler can only be sure that the argument is of compatible type if the expression is null. However, you can of course modify the collection by calling clear() or remove(Object).

    On the other hand, if you read from a Collection<? super Dog>, its iterator has return type ? super Dog. That is, it will return objects that are a subtype of some unknown supertype of Dog. But differently, the Collection could be a Collection<Object> containing only instances of String. Therefore

    for (Dog d : collection) { ... } // does not compile
    

    so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is

    for (Object o : collection) { ... }
    

    but it is possible to read from a collection, you just don't know what types of objects you will get.

    We can easily generalize that observation to: Given

    class G<T> { ... }
    

    and

    G<? extends Something> g;
    

    we can only pass null to method parameters with declared type T, but we can invoke methods with return type T, and assign the result a variable of type Something.

    On the other hand, for

    G<? super Something> g;
    

    we can pass any expression of type Something to method parameters with declared type T, and we can invoke methods with return type T, but only assign the result to a variable of type Object.

    To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.