Considere as duas classes e a interface a seguir:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
Por que a segunda chamada para mandatory
invocar o método sobrecarregado com Class2
, se getInterface1
e Interface1
não tem relacionamento 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;
}
}
Entendo que o Java 8 quebrou a compatibilidade com o 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
E com Java 8 (também testado com 11 e 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
Respostas:
As regras de inferência de tipo receberam uma revisão significativa no Java 8, principalmente a inferência de tipo de destino foi muito aprimorada. Portanto, enquanto antes do Java 8 o site de argumentos do método não recebia nenhuma inferência, padronizando o tipo apagado (
Class1
paragetClass1()
eInterface1
paragetInterface1()
), no Java 8 o tipo aplicável mais específico é inferido. O JLS para Java 8 introduziu um novo capítulo, capítulo 18. Inferência de tipo que está faltando no JLS for Java 7.O tipo aplicável mais específico para
<T extends Interface1>
é<X extends RequiredClass & BottomInterface>
, ondeRequiredClass
é uma classe requerida por um contexto eBottomInterface
é um tipo inferior para todas as interfaces (inclusiveInterface1
).Nota: Cada tipo Java pode ser representado como
SomeClass & SomeInterfaces
. Uma vez queRequiredClass
é subtipo deSomeClass
, eBottomInterface
é subtipo deSomeInterfaces
,X
é subtipo de todo tipo Java. Portanto,X
é um tipo inferior de Java.X
corresponde a ambos ospublic static <T> void mandatory(T o)
epublic static <T extends Class2> void mandatory(T o)
métodos assinaturas desdeX
é Java tipo de fundo.Portanto, de acordo com §15.12.2 ,
mandatory(getInterface1())
chama a sobrecarga mais específica domandatory()
método, que épublic static <T extends Class2> void mandatory(T o)
uma vez que<T extends Class2>
é mais específica que<T>
.Aqui está como você pode especificar explicitamente o
getInterface1()
parâmetro type para fazê-lo retornar o resultado que corresponde àpublic static <T extends Class2> void mandatory(T o)
assinatura do método:O tipo aplicável mais específico para
<T extends Class1>
é<Y extends Class1 & BottomInterface>
, ondeBottomInterface
é um tipo inferior para todas as interfaces.Y
corresponde àpublic static <T> void mandatory(T o)
assinatura do método, mas não corresponde àpublic static <T extends Class2> void mandatory(T o)
assinatura do método, poisY
não se estendeClass2
.Então
mandatory(getClass1())
chamapublic static <T> void mandatory(T o)
método.Ao contrário de
getInterface1()
, você não pode especificar explicitamente ogetClass1()
parâmetro type para fazê-lo retornar o resultado que corresponde àpublic static <T extends Class2> void mandatory(T o)
assinatura do método:fonte