使用两个不同的泛型参数调用泛型函数仍然可以编译

时间:2012-03-29 15:37:17

标签: java generics compilation compiler-errors

以下代码甚至可以编译怎么可能?据我所知,count函数是用两种不同的类型调用的,但编译器没有抱怨并且很乐意编译这段代码。

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

5 个答案:

答案 0 :(得分:7)

T被强制向上ObjectInteger[]可以上传至Object[]String上传至Object,并且可以进行类型检查。

答案 1 :(得分:2)

在这种情况下,T没用。您可以将签名更改为public static int count(Object[] x, Object y),而不会影响编译器允许它接受的参数。 (您可以看到Arrays.fill()的签名使用该签名作为签名。)

如果我们考虑更简单的情况,你只有T类型的参数,你可以看到,因为T的任何实例也是其超类的实例,T总是可以推断为它的上限,它仍然会接受与以前相同的参数类型。因此,我们可以摆脱T并使用其上限(在本例中为Object)。

Java中的数组工作方式相同:数组是协变的,这意味着如果ST的子类,S[]T[]的子类。因此,上述相同的论点适用 - 如果您只有类型TT[]的参数,T可以替换为其上限。

(请注意,这不适用于非协变或逆变的泛型类型:List<S>不是List<T>的子类型。)

答案 2 :(得分:2)

如果您将通话更改为:

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

你会看到编译器抱怨。

答案 3 :(得分:1)

通过一次传递两个对象,您在T上设置了太多限制。这会“强制”编译器推断Object。幸运的是,有一个简单的解决方法 - 只传递一个对象。以下内容将产生预期的错误。

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

答案 4 :(得分:0)

类型没有那么不同 - 两者都是java.lang.Object的子类。因此编译器假设在这种情况下T是Object。