目标类型具有通配符时的通用方法类型推断
我知道编译器使用目标类型来确定使generics方法调用适用的类型参数。 例如,在以下声明中:
List listOne = Collections.emptyList();
其中Collections.emptyList
在其签名中具有类型参数T
public static final List emptyList() {
在这种情况下, T
的推断类型参数是String
。
现在考虑以下内容:
List listTwo = Collections.emptyList();
在这种情况下,推断类型是什么? 是Object
吗? 或者它并不重要,因为通配符告诉编译器任何类型都是可能的?
通配符的每种用法都具有与之关联的不同类型。 (通常JLS将此称为“新类型”。)例如,这样的编译器错误如何工作:
List> list0 = ... ; List> list1 = ... ; list0.add(list1.get(0)); // error
因为list0
情况下, list0
和list1
由编译器给出了单独的类型
reference_type_of(List>) != reference_type_of(List>)
如果你尝试类似的东西,你可以开始看看它如何适合类型推断
{ List> list0 = ... ; List> list1 = ... ; test(list0, list1); } static void test(List list0, List list1) {}
编译器发出的错误实际上告诉我们它为list0
和list1
生成的类型。
错误:类Ideone中的方法测试不能应用于给定类型; test(list0,list1); ^ required:List,List 找到:列出 ,列出 原因:没有类型变量T的实例存在 参数类型List符合forms参数类型List 其中T是一个类型变量: T扩展方法 test(List ,List )中声明的Object 其中CAP#1,CAP#2是新的类型变量: CAP#1扩展了Object的捕获? CAP#2扩展了Object的捕获?
(我的重点是粗体。)这些CAP#...
类型是在捕获转换期间生成的。 它向我们展示的是,当检查方法调用表达式时, list0
和list1
被赋予彼此不同的类型。 (对于那些需要对错误进行解释的人:这是因为test
声明断言两个列表必须具有相同的T
)
因此我们现在知道通配符与引用类型相关联,我们可以在类似的情况下看到
List> empty = Collections.emptyList();
调用将被推断为类似“上限为Object的新类型”。 或者象征性地我们可以说编译器可能会看到类似的东西
// target type --> inferred invocation type // vv List empty = Collections.emptyList();
虽然:当然我们总是猜测一点,因为它取决于编译器如何实现它。 理论上,对于类似上面的emptyList()
简单赋值的emptyList()
,它不必做生成正确字节码的工作。
另外,对不起,我今天感觉不像规格。 本质上,类型推断通过生成一组约束来certificate方法调用应该或不应该编译。 18.5.2中描述的算法以这种方式包含通配符。
在这种情况下,推断类型是什么? 是对象吗? 或者它并不重要,因为通配符告诉编译器任何类型都是可能的?
在一个层面上,这是一个哲学问题,因为类型参数对编译的字节码没有任何影响,所以它具体是什么并不重要。 唯一重要的是,是否不可能满足界限和背景。 只要编译器可以certificate存在某种可以工作的类型,那么在我看来,它应该能够继续编译它而不需要提出实际类型。