首页 > 解决方案 > Java 中如何推断泛型类型?

问题描述

Function.identity()返回一个函数,该函数Function<T, T>始终返回其输入参数(即标识函数)。

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

我的思考过程图解:

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

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


根据OpenJDK,实现类似于:

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

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

标签: javagenericsjava-streamtype-inference

解决方案


Java 类型推理算法基于对推理变量的约束公式的解析。它在Java 语言规范的第 18 章中有详细描述。这有点涉及。

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

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

那么返回一个用作参数identity()的实例。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,我们将其称为βand γ,所以:{α :> β, α <: γ}。为避免陷入细节困境,让我们β只做一遍。

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

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

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

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

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

因此,通过推理过程,我们发现类型变量 forFunction.identity需要是Person或 的超类型Person。类似的过程α <: γ会产生{α <: Person}(如果指定了返回类型)。所以我们有两个约束:

  • α需要是Person或 的超类型Person;
  • α需要是;Person的子类型Person

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


推荐阅读