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}
(其中 :>
表示超类型,反之亦然)。这现在需要我们在这个表达式中推断 T
和 K
,我们将它们称为 β
和 γ
,所以:{α :> β, α <: γ}
。为了避免陷入细节的泥潭,让我们只研究 β
。
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 类型)。所以我们有两个约束:
α
需要是 Person
或 Person
的超类型;
α
需要是 Person
或 Person
的子类型;
显然,唯一满足所有这些约束的类型是 Person
。
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}
(其中 :>
表示超类型,反之亦然)。这现在需要我们在这个表达式中推断 T
和 K
,我们将它们称为 β
和 γ
,所以:{α :> β, α <: γ}
。为了避免陷入细节的泥潭,让我们只研究 β
。
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 类型)。所以我们有两个约束:
α
需要是Person
或Person
的超类型;α
需要是Person
或Person
的子类型;
显然,唯一满足所有这些约束的类型是 Person
。