Rozważ następujące dwie klasy i interfejs:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
Dlaczego drugie wywołanie mandatory
wywołuje przeciążoną metodę Class2
, jeśli getInterface1
i Interface1
nie ma związku z 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;
}
}
Rozumiem, że Java 8 złamała kompatybilność z Javą 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
I z Javą 8 (testowany również z 11 i 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
Odpowiedzi:
Reguły wnioskowania o typach zostały znacznie zmienione w Javie 8, przede wszystkim znacznie poprawiono wnioskowanie o typach docelowych. Tak więc, podczas gdy przed Java 8 witryna argumentów metody nie otrzymała żadnego wnioskowania, domyślnie jest to typ wymazany (
Class1
forgetClass1()
iInterface1
forgetInterface1()
), w Javie 8 wywodzi się najbardziej odpowiedni typ. JLS dla Java 8 wprowadził nowy rozdział Rozdział 18. Wnioskowanie typu, którego brakuje w JLS dla Java 7.Najbardziej konkretnym typem stosowanym dla
<T extends Interface1>
jest<X extends RequiredClass & BottomInterface>
, gdzieRequiredClass
jest klasa wymagana przez kontekst, iBottomInterface
jest typem dolnym dla wszystkich interfejsów (w tymInterface1
).Uwaga: Każdy typ Java może być reprezentowany jako
SomeClass & SomeInterfaces
. PonieważRequiredClass
jest podtypemSomeClass
iBottomInterface
jest podtypemSomeInterfaces
,X
jest podtypem każdego typu Java. DlategoX
jest typem Java na dole.X
dopasowuje obie sygnaturypublic static <T> void mandatory(T o)
ipublic static <T extends Class2> void mandatory(T o)
metody, ponieważX
jest to dolny typ Java.Tak więc, zgodnie z §15.12.2 ,
mandatory(getInterface1())
wywołuje najbardziej specyficzne przeciążeniemandatory()
metody, które jestpublic static <T extends Class2> void mandatory(T o)
odtąd<T extends Class2>
bardziej szczegółowe niż<T>
.Oto jak możesz jawnie określić
getInterface1()
parametr type, aby zwracał wynik zgodny zpublic static <T extends Class2> void mandatory(T o)
podpisem metody:Najbardziej konkretnym typem stosowanym dla
<T extends Class1>
jest<Y extends Class1 & BottomInterface>
, gdzieBottomInterface
jest typ dolny dla wszystkich interfejsów.Y
dopasowujepublic static <T> void mandatory(T o)
podpis metody, ale nie pasuje dopublic static <T extends Class2> void mandatory(T o)
podpisu metody, ponieważY
nie jest rozszerzanyClass2
.Tak
mandatory(getClass1())
wywołujepublic static <T> void mandatory(T o)
metodę.W przeciwieństwie do
getInterface1()
, nie można jawnie określićgetClass1()
parametru type, aby zwracał wynik zgodny zpublic static <T extends Class2> void mandatory(T o)
podpisem metody:źródło