繁体   English   中英

目标类型具有通配符时的泛型方法类型推断

[英]Generic method type inference when the target type has a wildcard

我知道编译器使用目标类型来确定使通用方法调用适用的类型参数。 例如,在以下语句中:

List<String> listOne = Collections.emptyList();

其中Collections.emptyList在其签名中具有类型参数T

public static final <T> List<T> emptyList() {

在这种情况下, T的推断类型参数为String

现在考虑以下几点:

List<?> listTwo = Collections.emptyList();

在这种情况下,推断的类型是什么? Object吗? 还是因为通配符告诉编译器任何类型都是可能的?

通配符的每种用法都有与之关联的不同类型。 (通常,JLS将此称为“新鲜类型”。)例如,这就是这样的编译器错误的工作方式:

List<?> list0 = ... ;
List<?> list1 = ... ;
list0.add(list1.get(0)); // error

因为在这种情况下,编译器会为list0list1分别指定不同的类型,因此大多数情况下

reference_type_of(List<?>) != reference_type_of(List<?>)

如果您尝试类似的操作,则可以开始了解它如何适合于推论

{
    List<?> list0 = ... ;
    List<?> list1 = ... ;
    test(list0, list1);
}
static <T> void test(List<T> list0, List<T> list1) {}

编译器发出错误的地方实际上告诉我们一些有关list0list1生成的类型的信息。

error: method test in class Ideone cannot be applied to given types;
    test(list0, list1);
    ^
  required: List<T>,List<T>
  found: List<CAP#1>,List<CAP#2>
  reason: no instance(s) of type variable(s) T exist so that
          argument type List<CAP#2> conforms to formal parameter type List<T>
  where T is a type-variable:
    T extends Object declared in method <T>test(List<T>,List<T>)
  where CAP#1,CAP#2 are fresh type-variables:
    CAP#1 extends Object from capture of ?
    CAP#2 extends Object from capture of ?

(我的粗体为粗体。)这些CAP#...类型是在捕获转换期间生成的。 它向我们显示的是,当检查方法调用表达式时, list0list1彼此具有不同的类型。 (对于那些需要解释错误的人:这是因为test声明断言两个List必须具有相同的T

因此,由于我们现在知道通配符与引用类型相关联,因此可以看到

List<?> empty = Collections.emptyList();

调用将被推断为“上限为对象的新鲜类型”。 或象征性地,我们可以说编译器可能会看到类似

// target type       -->       inferred invocation type
//     v                           v
List<CAP#1> empty = Collections.<CAP#1>emptyList();

尽管:当然,我们总是会猜测一点,因为这取决于编译器如何实现。 从理论上讲,对于上述琐碎的emptyList()赋值情况,它不必做任何工作来生成正确的字节码。

另外,很抱歉,我今天不喜欢规格检查。 本质上,这里的类型推断通过生成一组约束来证明方法调用应该或不应该编译而起作用。 18.5.2中描述的算法以这种方式合并了通配符。

在这种情况下,推断的类型是什么? 是对象吗? 还是因为通配符告诉编译器任何类型都是可能的?

在一个层面上,这是一个哲学问题,因为类型实参对编译后的字节码没有任何影响,因此它的具体含义并不重要。 唯一重要的是是否无法满足界限和上下文。 只要编译器可以证明存在某种可以使用的类型,那么我认为,它应该能够继续进行编译,而无需提出实际的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM