Статические параметризованные методы в Java

#java #generics

#java #дженерики

Вопрос:

Я думал, что понимаю дженерики. Однако в этом примере я не могу понять, почему строка isIn1(new Class2(1), arr); компилируется, а строка isIn2(new Class2(1), arr); не компилируется.

 class E1{};
class E2{};

class Class1 implements Comparable<Class1> {
    int i;

    public Class1(int i) {
        this.i = i;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Class1 class1 = (Class1) o;
        return i == class1.i;
    }

    @Override
    public int hashCode() {
        return Objects.hash(i);
    }

    @Override
    public int compareTo(Class1 o) {
        return this.i - o.i;
    }
}

class Class2 implements Comparable<Class2> {
    int i;

    public Class2(int i) {
        this.i = i;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Class2 class2 = (Class2) o;
        return i == class2.i;
    }

    @Override
    public int hashCode() {
        return Objects.hash(i);
    }

    @Override
    public int compareTo(Class2 o) {
        return this.i - o.i;
    }
}


public class G05Demo2 {

    public static <T, V extends T> void isIn1(T x, V[] y) {
        for (int i = 0; i < y.length; i  ) {
            if (x.equals(y[i])) {
                System.out.println(" Yes, is in ");
            } else {
                System.out.println(" Not is in ");
            }
        }
    }
    public static <T extends Comparable<T>, V extends T> void isIn2(T x, V[] y) {
        for (int i = 0; i < y.length; i  ) {
            if (x.equals(y[i])) {
                System.out.println(" Yes, is in ");
            } else {
                System.out.println(" Not is in ");
            }
        }
    }

    public static void main(String args[]) {

        Class1[] arr = {new Class1(1)};

        isIn1(new Class1(1), arr); //  Yes, is in
        isIn1(new Class2(1), arr); //  Not is in    - why?

        isIn2(new Class1(1), arr); //  Yes, is in
        isIn2(new Class2(1), arr); //  NOT compiled - why?

        G05Demo2.<Class1, Class1>isIn1(new Class1(1), arr); //  Yes, is in
        G05Demo2.<Class2, Class1>isIn1(new Class2(1), arr); //  NOT compiled (type checking works, since they are explicitly specified)

        G05Demo2.<Class1, Class1>isIn2(new Class1(1), arr); //  Yes, is in
        G05Demo2.<Class2, Class1>isIn2(new Class2(1), arr); //  NOT compiled (type checking works, since they are explicitly specified)

        E1[] arr2 = {new E1()};
        isIn1(new E2(), arr2);

    }
}
 

Комментарии:

1. Class1 и Class2 не связаны между собой, поэтому, когда V равно Class1, а T равно Class2, V расширяет T не выполняется.

2. Обратите внимание, что это не имеет ничего общего со статичностью. Игрушки получают точно то же самое с универсальными методами экземпляра.

Ответ №1:

 isIn1(new Class2(1), arr);
 

выводит «Not is in», потому Class2.equals что возвращает false , когда this и o не являются экземплярами одного и того же класса:

 @Override
public boolean equals(Object o) {
    if (this == o) return true;

    //          here VVVVVVVVVVVVVVVVVVVVVVVVVV
    if (o == null || getClass() != o.getClass()) return false;
    Class2 class2 = (Class2) o;
    return i == class2.i;
}
 

arr содержит экземпляр Class1 , но вы передали экземпляр Class2 в качестве первого параметра isIn1 .


 isIn2(new Class2(1), arr);
 

Не компилируется, потому что нет типов, которые удовлетворяют указанным вами ограничениям isIn2 , которые могут быть выведены. В частности, должен быть тип T , который является подтипом Comparable<T> , и тип V , который является подтипом T . Class2 также должны быть совместимы с T и Class1[] должны быть совместимы с. V[]


«Как насчет T is Class2 и V is Class1 ?» вы могли бы сказать:

 Main.<Class2, Class1>isIn2(new Class2(1), arr);
 

Это не работает, потому Class1 что не является подтипом Class2 . Они вообще не связаны! Аналогичный isIn1 вызов завершается ошибкой по той же причине.


Почему

 isIn1(new Class2(1), arr);
 

затем скомпилировать?

Не указывая параметры универсального типа, компилятор может сделать все возможное, чтобы вывести типы, чтобы код компилировался. Также обратите внимание, что isIn1 имеет на одно ограничение меньше, чем isIn2 .

Здесь одним из решений является T is Comparable<? extends Comparable<?>> и V is Class1 .

 Main.<Comparable<? extends Comparable<?>>, Class1>isIn1(new Class2(1), arr);
 

Вы можете проверить, что они удовлетворяют ограничениям, используя правила вспомогательной типизации, описанные в разделе 4.10 JLS.

Ответ №2:

Для isIn1 , оба T и V могут быть выведены как Object . (Из-за этого в этих переменных типа нет смысла, вы также можете использовать Object явно).

For isIn2 , T выводится как Class1 ; поскольку Class2 не расширяет это, компилятор отклоняет код.