How do generic types get inferred in Java?(Java 中如何推断泛型类型?)
问题描述
The Function.identity() returns a function, Function<T, T> that always returns its input argument(i.e. identity function).
But being a static method, how does it know which concrete argument to return in place of type parameter T when it doesn't even take any inputs?
Illustration of my thought process:
Map idToPerson = people.collect( Collectors.toMap( (person -> person.getID() , Function.identity() ) );
Question: So how does the compiler figure out that Function.identity() is supposed to return Function<element of 'people' stream, element of 'people' stream> stream despite having no inputs?
According to the OpenJDK, the implementation is something like:
static <T> Function<T, T> identity()
{
return t -> t;
}
An attempt to narrow down my question:
How does Function.identity() know what the concrete data type t in t -> t(btw this is the lambda Function<T, T>) is?
The Java type inference algorithm is based on the resolution of constraint formulas on inference variables. It is described in detail in Chapter 18 of the Java Language Specification. It's a bit involved.
Informally, for the example above the reasoning would go roughly as follows:
We have an invocation of Function.<T>identity(). Because most type parameters are named T, and consistently with the JLS, I'll use Greek letters to denote inference variables. So in this initial expression T :: α. What constraints do we have on α?
Well identity() returns an instance of Function<α,α> used as argument to toMap.
static <T,K,U> Collector<T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,
Function<? super T,? extends U> valueMapper)
So now we have the constraints {α :> T, α <: K} (where :> means supertype of and vice-versa). This now requires us to infer T and K in this expression, which we'll refer to as β and γ, so: {α :> β, α <: γ}. To avoid getting bogged down in details, let's work through β only.
toMap then returns a collector as argument to Stream.collect, which provides us with another source of constraints:
collect(Collector<? super T,A,R> collector)
So now we know that {β :> T}. But here T also needs to be inferred, so it becomes an inference variable, and we have {β :> δ}.
This is where it starts unfolding, because the type parameter T for method collect refers to the parameter T in Stream<T>. So assuming the stream was defined as Stream<Person>, now we have {δ=Person} and we can reduce as follows:
{β :> δ} => {β :> Person}(βis a supertype ofPerson);{α :> β} => {α :> (β :> Person)} => {α :> Person)}(αis a supertype ofPerson);
So through the process of inference we figured out that the type variable for Function.identity needs to be Person or a supertype of Person. A similar process for α <: γ would yield {α <: Person} (if the return type is specified). So we have two constraints:
αneeds to bePersonor a supertype ofPerson;αneeds to bePersonor a subtype ofPerson;
Clearly the only type that satisfies all these constraints is Person.
这篇关于Java 中如何推断泛型类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:Java 中如何推断泛型类型?
基础教程推荐
- Java 实例变量在两个语句中声明和初始化 2022-01-01
- Java Swing计时器未清除 2022-01-01
- 多个组件的复杂布局 2022-01-01
- 不推荐使用 Api 注释的描述 2022-01-01
- 在 Java 中创建日期的正确方法是什么? 2022-01-01
- 从 python 访问 JVM 2022-01-01
- 如何在 JFrame 中覆盖 windowsClosing 事件 2022-01-01
- 验证是否调用了所有 getter 方法 2022-01-01
- 如何在 Spring @Value 注解中正确指定默认值? 2022-01-01
- 大摇大摆的枚举 2022-01-01
