Вызов универсальной функции с двумя различными универсальными аргументами все еще компилируется - PullRequest
4 голосов
/ 29 марта 2012

Как это возможно, что следующий код даже компилируется? Насколько я вижу, функция 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 ]

7 голосов
/ 29 марта 2012

T принудительно увеличивается до Object. Значение Integer[] может быть повышено до Object[], а значение String повышено до Object, и оно проверяется.

2 голосов
/ 29 марта 2012

Если вы измените свой вызов на:

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

Вы увидите жалобу компилятора.

2 голосов
/ 29 марта 2012

В этом случае T бесполезен.Вы можете изменить подпись на public static int count(Object[] x, Object y) без какого-либо влияния на аргументы, которые компилятор разрешит ему принять.(Вы можете видеть, что подпись для Arrays.fill() использует ее в качестве подписи.)

Если мы рассмотрим более простой случай, когда у вас просто есть аргументы типа T, вы можете увидеть это, поскольку любой экземплярT также является экземпляром своих суперклассов, T всегда может быть выведен как его верхняя граница, и он все равно будет принимать те же типы аргументов, что и раньше.Таким образом, мы можем избавиться от T и использовать вместо него его верхнюю границу (в данном случае Object).

Массивы в Java работают так же: массивы ковариантны, что означает, что если Sподкласс T, S[] является подклассом T[].Таким образом, применяется тот же аргумент, что и выше - если у вас есть только аргументы типа T и T[], T можно заменить его верхней границей.

(Обратите внимание, что это не относится к универсальномутипы, которые не являются ковариантными или контравариантными: List<S> не является подтипом List<T>.)

1 голос
/ 30 марта 2012

Передавая два объекта одновременно, вы накладываете слишком много ограничений на 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;
    }
}
0 голосов
/ 29 марта 2012

Типы не так уж отличаются - оба являются подклассами java.lang.Object. Таким образом, компилятор предполагает, что T является Object в этом случае.

...