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

标签 java generics type-inference

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

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 ?

(My emphasis in bold.) These CAP#... types were generated during capture conversion. What it's showing us is that when the method invocation expression was examined, list0 and list1 were given distinct types from each other. (And for those that need an explanation for the error: it's because the declaration of test asserts that both Lists must have the same T.)

So since we now know that a wildcard gets associated a reference type, we can see that in a case like

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

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

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

虽然:当然我们总是会猜测一点,因为这取决于编译器如何实现它。理论上,对于像上述 emptyList() 的简单赋值这样的情况,它不需要做任何工作来生成正确的字节码。

另外,很抱歉,我今天不想进行规范搜索。本质上,这里的类型推断通过生成一组约束来证明方法调用应该或不应该编译。 18.5.2中描述的算法以这种方式合并通配符。

关于java - 目标类型具有通配符时的泛型方法类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27878132/

相关文章:

java - 基于方法参数的反 boolean 结果

java - 将 SQLite 数据库复制到 Android 设备 C#

java - 我应该如何递归地编写这个Java函数?

C#泛型,传递类

java - 何时在 Java 泛型中使用通配符?

f# - 为什么 F# 在这个函数中推断 int?

c# - 扩展方法和类型推断

c# - 还有哪些其他语言支持 Go 的接口(interface)风格而无需显式声明?

java - 如何修复 java Apache POI 中的 NotOfficeXmlFileException?

java - 我将如何在 Java 中使用泛型来创建一个泛型接口(interface),该接口(interface)定义了一个只接受实现作为参数的方法?