Calling generic function with two different generic arguments still compiles

喜夏-厌秋 提交于 2019-12-12 11:25:35

问题


How is it possible that following code even compiles? As far as I can see the count function is called with two different types, yet compiler doesn't complain and happily compiles this code.

public class Test {
        public static <T> int count(T[] x,T y){
                int count = 0;
                for(int i=0; i < x.length; i++){
                        if(x[i] == y) count ++;
                }
                return count;  
        }
        public static void main(String[] args) {
                Integer [] data = {1,2,3,1,4};
                String value = "1";
                int r =count(data,value);
                System.out.println( r + " - " + value);
        }
}

回答1:


In this case the T is useless. You can change the signature to public static int count(Object[] x, Object y) without any effect on what arguments the compiler will let it accept. (You can see that the signature for Arrays.fill() uses that as the signature.)

If we consider the simpler case, where you just have arguments of type T, you can see that, since any instance of T is also an instance of its superclasses, T can always to be inferred to be its upper bound, and it will still accept the same argument types as before. Thus we can get rid of T and use its upper bound (in this case Object) instead.

Arrays in Java work the same way: arrays are covariant, which means that if S is a subclass of T, S[] is a subclass of T[]. So the same argument as above applies -- if you just have arguments of type T and T[], T can be replaced by its upper bound.

(Note that this does not apply to generic types, which are not covariant or contravariant: List<S> is not a subtype of List<T>.)




回答2:


T gets coerced upwards to Object. The Integer[] can be upcasted to Object[], and the String gets upcasted to Object, and it typechecks.




回答3:


If you change your call to:

int r = Test.<Integer>count(data, value);

You will see the compiler complain.




回答4:


By passing two objects at once you are putting up too many constraints on T. This "forces" the compiler to infer Object. Luckily there's a simple workaround -- only pass one object. The following will produce the expected error.

public static void main(String[] args) {
    Integer[] data = { 1, 2, 3, 4 };
    String value = "1";
    int r = count(value).in(data);
    System.out.println(r + " - " + value);
}

public static <T> Counter<T> count(T obj) {
    return new Counter<T>(obj);
}

public static class Counter<T> {
    private final T obj;

    Counter(T obj) {
        this.obj = obj;
    }

    public int in(T[] array) {
        return in(Arrays.asList(array));
    }

    public int in(Iterable<? extends T> iterable) {
        int count = 0;
        for (T element : iterable) {
            if (element == obj) {
                ++count;
            }
        }
        return count;
    }
}



回答5:


Types are not so different - both are subclasses of java.lang.Object. So compiler assumes T is Object in this case.



来源:https://stackoverflow.com/questions/9928723/calling-generic-function-with-two-different-generic-arguments-still-compiles

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!