Рассмотрим следующие два класса и интерфейс:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
Почему второй вызов mandatory
вызывает перегруженный метод с Class2
, если getInterface1
и Interface1
не имеет отношения с Class2
?
public class Test {
public static void main(String[] args) {
Class1 class1 = getClass1();
Interface1 interface1 = getInterface1();
mandatory(getClass1()); // prints "T is not class2"
mandatory(getInterface1()); // prints "T is class2"
mandatory(class1); // prints "T is not class2"
mandatory(interface1); // prints "T is not class2"
}
public static <T> void mandatory(T o) {
System.out.println("T is not class2");
}
public static <T extends Class2> void mandatory(T o) {
System.out.println("T is class2");
}
public static <T extends Class1> T getClass1() {
return null;
}
public static <T extends Interface1> T getInterface1() {
return null;
}
}
Я понимаю, что Java 8 нарушила совместимость с Java 7:
$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac -source 1.7 -target 1.7 *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
warning: [options] bootstrap class path not set in conjunction with -source 1.7
1 warning
T is not class2
T is not class2
T is not class2
T is not class2
И с Java 8 (также протестировано с 11 и 13):
$ /usr/lib/jvm/java-8-openjdk-amd64/bin/javac *java; /usr/lib/jvm/java-8-openjdk-amd64/bin/java Test
T is not class2
T is class2
T is not class2
T is not class2
Ответы:
Правила вывода типов получили существенную переработку в Java 8, в частности, вывод целевого типа был значительно улучшен. Таким образом, в то время как до Java 8 сайт с аргументом метода не получал никакого вывода, по умолчанию стертый тип (
Class1
дляgetClass1()
иInterface1
дляgetInterface1()
), в Java 8 выводится наиболее конкретный применимый тип. JLS для Java 8 представил новую главу Глава 18. Вывод типа, который отсутствует в JLS для Java 7.Наиболее конкретный применимый тип для
<T extends Interface1>
- это<X extends RequiredClass & BottomInterface>
, гдеRequiredClass
класс, требуемый контекстом, иBottomInterface
нижний тип для всех интерфейсов (включаяInterface1
).Примечание. Каждый тип Java может быть представлен как
SomeClass & SomeInterfaces
. Так какRequiredClass
является подтипомSomeClass
иBottomInterface
является подтипомSomeInterfaces
,X
является подтипом каждого типа Java. Таким образом,X
является типом дна Java.X
соответствует обеим сигнатурамpublic static <T> void mandatory(T o)
иpublic static <T extends Class2> void mandatory(T o)
сигнатурам методов, посколькуX
является типом дна Java.Так, по словам §15.12.2 ,
mandatory(getInterface1())
вызывает наиболее специфические перегружатьmandatory()
метод, который сpublic static <T extends Class2> void mandatory(T o)
тех пор<T extends Class2>
является более конкретным , чем<T>
.Вот как вы можете явно указать
getInterface1()
параметр типа, чтобы он возвращал результат, соответствующийpublic static <T extends Class2> void mandatory(T o)
сигнатуре метода:Наиболее конкретный применимый тип для
<T extends Class1>
- это<Y extends Class1 & BottomInterface>
гдеBottomInterface
нижний тип для всех интерфейсов.Y
соответствуетpublic static <T> void mandatory(T o)
сигнатуре метода, но не соответствуетpublic static <T extends Class2> void mandatory(T o)
сигнатуре метода, посколькуY
не расширяетсяClass2
.Так
mandatory(getClass1())
называетpublic static <T> void mandatory(T o)
метод.В отличие от with
getInterface1()
, вы не можете явно указатьgetClass1()
параметр типа, чтобы он возвращал результат, соответствующийpublic static <T extends Class2> void mandatory(T o)
сигнатуре метода:источник