Java 中如何推断泛型类型?

How do generic types get inferred in Java?

Function.identity()return是一个函数,Function<T, T>总是return它的输入参数(即恒等函数) .

但是作为一个静态方法,当它甚至不接受任何输入时,它如何知道 return 的哪个具体参数代替类型参数 T

我的思维过程图解:

Map idToPerson = people.collect( Collectors.toMap( (person -> person.getID() , Function.identity() ) );

问题:那么尽管没有输入,编译器如何确定 Function.identity() 应该 return Function<element of 'people' stream, element of 'people' stream> 流?


根据 OpenJDK,实现类似于:

static <T> Function<T, T> identity()                                                                                                                          
{ 
    return t -> t;    
}

试图缩小我的问题范围:
Function.identity() 如何知道 t -> t 中的具体数据类型 t(顺便说一句,这是 lambda Function<T, T>)是什么?

Java类型推理算法基于约束公式推理变量的解析。在 Chapter 18 of the Java Language Specification 中有详细描述。有点牵强。

非正式地,对于上面的例子,推理大致如下:

我们调用了 Function.<T>identity()。因为大多数类型参数被命名为 T,并且与 JLS 一致,所以我将使用希腊字母来表示推理变量。所以在这个初始表达式中 T :: α。我们对 α 有什么限制?

好吧 identity() return 一个 Function<α,α> 的实例用作 toMap 的参数。

static <T,K,U> Collector<T,?,Map<K,U>>  toMap(Function<? super T,? extends K> keyMapper, 
   Function<? super T,? extends U> valueMapper)

所以现在我们有了约束 {α :> T, α <: K}(其中 :> 表示超类型,反之亦然)。这现在需要我们在这个表达式中推断 TK,我们将它们称为 βγ,所以:{α :> β, α <: γ}。为了避免陷入细节的泥潭,让我们只研究 β

toMap 然后 return 收集器作为 Stream.collect 的参数,这为我们提供了另一个约束源:

collect(Collector<? super T,A,R> collector)

所以现在我们知道 {β :> T}。但是这里T也需要推断,所以就成了推断变量,我们有{β :> δ}.

这是它开始展开的地方,因为方法collect的类型参数T引用了Stream<T>中的参数T。所以假设流被定义为 Stream<Person>,现在我们有 {δ=Person} 并且我们可以减少如下:

  • {β :> δ} => {β :> Person}βPerson 的超类型);
  • {α :> β} => {α :> (β :> Person)} => {α :> Person)}αPerson 的超类型);

所以通过推理我们得出Function.identity的类型变量需要是Person或者Person的超类型。 α <: γ 的类似过程将产生 {α <: Person}(如果指定了 return 类型)。所以我们有两个约束:

  • α 需要是 PersonPerson 的超类型;
  • α 需要是 PersonPerson 的子类型;

显然,唯一满足所有这些约束的类型是 Person