JDK 8 的类型推断如何与泛型一起工作?

How JDK 8's type inference works with generic?

我的代码使用 JDK 7 编译失败,但使用 JDK 8 编译成功。

抽象实际代码:

interface A {
...
}

class B implements A {
...
}

public void AAA(List<A> list) {...}

AAA(Collections.singletonList(new B()));

Collections.singletonList 定义为

public static <T> List<T> singletonList(T o) {
    return new SingletonList<>(o);
}

据我所知,基于泛型,T 将被推断为 B,因此 Collections.singletonList(new B()) 将是无法分配给 List 的列表,因为 Java 泛型是不变的。

但是对于JDK8,T被推断为A并且编译成功。

我想知道如何将 T 推断为 A,因为这里有两个类型 T 的变量:A 和 B。

有先后顺序吗?或者编译器是否找到共同祖先 class?

附上官方文档更好!

提前致谢!

ps1。 JDK7的版本是Oracle 1.7.0_79,JDK8的版本是Oracle 1.8.0_66.

ps2。这是实际代码的链接:

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/examples/storm-opentsdb-examples/src/main/java/org/apache/storm/opentsdb/SampleOpenTsdbBoltTopology.java#L48

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/OpenTsdbBolt.java#L77

https://github.com/apache/storm/blob/85a31e2fdec1ffef83e1ff438cd765a821fb06e4/external/storm-opentsdb/src/main/java/org/apache/storm/opentsdb/bolt/TupleOpenTsdbDatapointMapper.java#L37

这实际上在 here 最后在 目标类型 处进行了解释。

目标类型已扩展为包括方法参数...

这意味着

 AAA(Collections.singletonList(new B())); // returns List<A> NOT List<B>

在 jdk-7 方法参数中 而不是 用于找出目标类型,这就是为什么 T 被推断为 B 而失败的原因。

好吧,语言规范中有一个全新的章节 §18. Type Inference,但这并不容易阅读。即使是第一部分的总结,准确地解决了你的问题,也很难:

In comparison to the Java SE 7 Edition of The Java® Language Specification, important changes to inference include:

  • Adding support for lambda expressions and method references as method invocation arguments.
  • Generalizing to define inference in terms of poly expressions, which may not have well-defined types until after inference is complete. This has the notable effect of improving inference for nested generic method and diamond constructor invocations.
  • Describing how inference is used to handle wildcard-parameterized functional interface target types and most specific method analysis.
  • Clarifying the distinction between invocation applicability testing (which involves only the invocation arguments) and invocation type inference (which incorporates a target type).
  • Delaying resolution of all inference variables, even those with lower bounds, until invocation type inference, in order to get better results.
  • Improving inference behavior for interdependent (or self-dependent) variables.
  • Eliminating bugs and potential sources of confusion. This revision more carefully and precisely handles the distinction between specific conversion contexts and subtyping, and describes reduction by paralleling the corresponding non-inference relations. Where there are intentional departures from the non-inference relations, these are explicitly identified as such.
  • Laying a foundation for future evolution: enhancements to or new applications of inference will be easier to integrate into the specification.

第二个项目符号对您的代码示例影响最大。你有一个泛型方法的嵌套方法调用,没有指定显式类型参数,这使得它成为一个 so-called poly expression 其实际类型可以从 目标类型,在你的例子中是AAA的参数类型。

所以这是一个相当简单的星座,因为 AAA 不是通用的,并且关于它的参数类型没有歧义。总是 List<A>。这里没有搜索“共同祖先 class”,所有需要检查的是参数表达式的类型 (B) 是否与推断类型 (A) 兼容.

http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html

的 "target types" 部分

它说

This is no longer necessary in Java SE 8. The notion of what is a target type has been expanded to include method arguments, such as the argument to the method processStringList.

你的方法 AAA 需要类型 A 的列表,所以我相信只要你为调用 AAA() 提供的类型是可用的类型转换或 A 的子类型,Java8 的目标类型就会选择根据您在代码中给出的提示,A 超过 B?