#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
не расширяет это, компилятор отклоняет код.