Considere las siguientes dos clases e interfaces:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
¿Por qué la segunda llamada para mandatory
invocar el método sobrecargado con Class2
, si getInterface1
y Interface1
no tiene relación 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;
}
}
Entiendo que Java 8 rompió la compatibilidad con 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
Y con Java 8 (también probado con 11 y 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
Respuestas:
Las reglas de inferencia de tipos han recibido una revisión importante en Java 8, más notablemente la inferencia de tipos de destino se ha mejorado mucho. Entonces, mientras que antes de Java 8 el sitio de argumento del método no recibió ninguna inferencia, por defecto al tipo borrado (
Class1
porgetClass1()
yInterface1
paragetInterface1()
), en Java 8 se infiere el tipo aplicable más específico. JLS para Java 8 introdujo un nuevo capítulo Capítulo 18. Inferencia de tipo que falta en JLS para Java 7.El tipo aplicable más específico para
<T extends Interface1>
es<X extends RequiredClass & BottomInterface>
, dondeRequiredClass
es una clase requerida por un contexto, yBottomInterface
es un tipo inferior para todas las interfaces (incluidasInterface1
).Nota: cada tipo de Java se puede representar como
SomeClass & SomeInterfaces
. Dado queRequiredClass
es un subtipo deSomeClass
, yBottomInterface
es un subtipo deSomeInterfaces
,X
es un subtipo de cada tipo de Java. Por lo tanto,X
es un tipo de fondo Java.X
partidos ambospublic static <T> void mandatory(T o)
ypublic static <T extends Class2> void mandatory(T o)
métodos firmas yaX
es Java tipo de fondo.Así, de acuerdo con §15.12.2 ,
mandatory(getInterface1())
llama a la sobrecarga específica mayor parte demandatory()
método, que espublic static <T extends Class2> void mandatory(T o)
ya<T extends Class2>
es más específico que<T>
.Aquí se explica cómo puede especificar explícitamente el
getInterface1()
parámetro de tipo para que devuelva el resultado que coincide con lapublic static <T extends Class2> void mandatory(T o)
firma del método:El tipo aplicable más específico para
<T extends Class1>
es<Y extends Class1 & BottomInterface>
, dondeBottomInterface
es un tipo inferior para todas las interfaces.Y
coincide con lapublic static <T> void mandatory(T o)
firma del método, pero no coincide con lapublic static <T extends Class2> void mandatory(T o)
firma del método yaY
que no se extiendeClass2
.Entonces
mandatory(getClass1())
llamapublic static <T> void mandatory(T o)
método.A diferencia de
getInterface1()
, no puede especificar explícitamente elgetClass1()
parámetro de tipo para que devuelva el resultado que coincide con lapublic static <T extends Class2> void mandatory(T o)
firma del método:fuente