eric.mcgregor
eric.mcgregor

Reputation: 3653

In Java, why can I only restrict collection parameters using generics?

Below I have two generic methods. Both have two parameterized types T and V extends T. The first method, foo, takes arguments of type T and V. Strangely, I can call foo("hello", new Integer(10)) and it compiles and runs even though Integer clearly doesn't extend String. The second method, bar, takes arguments of type List< T > and List< V >. Here, I can not call bar, passing it a List of Strings and a List of Integers. Why does the latter restrict the type, and the former not.

public class GenMeth {

    public static void main(String[] args) {

        List<String> s_list = new ArrayList<>();
        s_list.add("hello");

        List<Integer> i_list = new ArrayList<>();
        i_list.add(10);

        foo("hello", new Integer(10));  // will compile - why?

        bar(s_list, i_list);  // won't compile - understandable

    }

    public static <T,V extends T> void foo(T obj1, V obj2) {
            // do something
    }

    public static <T,V extends T> void bar(List<T> list1, List<V> list2) {
            // do something
    }
}

Upvotes: 0

Views: 207

Answers (1)

newacct
newacct

Reputation: 122489

foo will always be able to take two arguments of any reference type, because T and V can be chosen to be Object, and any reference type is a subtype of Object. So foo might as well be declared as public static void foo(Object obj1, Object obj2); it would make no difference. The way it is declared, there is nothing that foo can do with either of its parameters except the things that can be done with Object, since the type variables are not bounded, so there is no reason for any restriction beyond that they are both Object.

bar is different because the type variable is in the type parameter. Generics are invariant -- List<String> is not a subtype of List<Object> (nor vice versa) even though String is a subtype of Object. So by having the first parameter be type List<T>, it forces T to match the type parameter of the passed argument exactly; i.e. by passing a List<String> as the first argument, it forces T to be String; it can't be Object or anything else -- it wouldn't be compatible. And since the type of the second parameter is List<V> and V extends T, it means the type parameter of the second argument must be String or a subtype of String. (p.s. bar can also be declared a little simpler as public static <T> void bar(List<T> list1, List<? extends T> list2))

Upvotes: 1

Related Questions