为什么编译器在用不相关的接口类型调用时选择这个带有 class 类型参数的泛型方法?
Why does the compiler choose this generic method with a class type parameter when invoked with an unrelated interface type?
考虑以下两个类和接口:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
如果getInterface1
和Interface1
与Class2
没有关系,为什么第二次调用mandatory
会调用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 中推断出最具体的适用类型。 Java 8 的 JLS 引入了 Chapter 18. Type Inference 的新章节,Java 7.
的 JLS 中缺少该章节
<T extends Interface1>
最具体的适用类型是<X extends RequiredClass & BottomInterface>
,其中RequiredClass
是上下文需要的class,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()
类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o)
方法签名的结果:
public static <T extends Class2 & Interface1> void helper() {
mandatory(Test.<T>getInterface1()); // prints "T is class2"
}
<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)
方法。
与 getInterface1()
不同,您不能显式指定 getClass1()
类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o)
方法签名的结果:
java: interface expected here
↓
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
mandatory(Test.<T>getClass1());
}
考虑以下两个类和接口:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
如果getInterface1
和Interface1
与Class2
没有关系,为什么第二次调用mandatory
会调用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
$ /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 中推断出最具体的适用类型。 Java 8 的 JLS 引入了 Chapter 18. Type Inference 的新章节,Java 7.
<T extends Interface1>
最具体的适用类型是<X extends RequiredClass & BottomInterface>
,其中RequiredClass
是上下文需要的class,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()
类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o)
方法签名的结果:
public static <T extends Class2 & Interface1> void helper() {
mandatory(Test.<T>getInterface1()); // prints "T is class2"
}
<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)
方法。
与 getInterface1()
不同,您不能显式指定 getClass1()
类型参数以使其 return 匹配 public static <T extends Class2> void mandatory(T o)
方法签名的结果:
java: interface expected here
↓
public static <T extends Class1 & C̲l̲a̲s̲s̲2> void helper() {
mandatory(Test.<T>getClass1());
}