简体   繁体   中英

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

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 ; ie 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 . (ps bar can also be declared a little simpler as public static <T> void bar(List<T> list1, List<? extends T> list2) )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM