java - 为什么编译器在使用不相关的接口类型调用时会选择带有类类型参数的泛型方法?
问题描述
考虑以下两个类和接口:
public class Class1 {}
public class Class2 {}
public interface Interface1 {}
为什么第二次调用mandatory
调用重载方法时与Class2
, ifgetInterface1
和与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
forgetClass1()
和Interface1
for getInterface1()
),但在 Java 8 中推断出最具体的适用类型。Java 8 的 JLS 引入了新的第 18 章。Java 7 的 JLS 中缺少类型推断。
最具体的适用类型<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)
方法签名匹配的结果:
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)
方法。
与 with 不同getInterface1()
,您不能显式指定getClass1()
类型参数以使其返回与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());
}
推荐阅读
- python - 用于可变长度序列的 LSTM 变分自动编码器
- python-3.x - Python、TCP、服务器和客户端
- hadoop - 主从系统操作系统版本
- string - 在 perl 中比较两个字符串并找到不匹配和匹配并计算它们
- python-3.x - 如何在 python 中运行程序时跳过我的 .txt 文件中的空白行
- javascript - 为函数 onclick getelementbyid 添加平滑滚动
- android - 在导航抽屉菜单项上使用 TapTargetView
- ios - 如何将异步方法变成同步方法
- python - 如何将整数添加到列表中的某些元素?
- sql - 无法从 Sql Server 表中获取最近的经纬度