引擎盖下
使用一些隐藏的功能,我们可以获得有关正在发生的事情的更多信息:javac
$ javac -XDverboseResolution=deferred-inference,success,applicable LambdaInference.java
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Object>)Object
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: error: cannot find symbol
Foo.foo(value -> true).booleanValue(); // Compile error here
^
symbol: method booleanValue()
location: class Object
1 error
这是很多信息,让我们分解一下。
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
阶段:方法适用性阶段
实际情况:在类型参数中
传递的实际参数:显式类型参数
候选:可能适用的方法
actuals 是因为我们的隐式类型 lambda 与适用性无关。<none>
编译器会将 您对 的调用解析为 中唯一命名的方法。它已被部分实例化为(因为没有实际值或类型参数),但这可以在延迟推理阶段更改。foo
foo
Foo
Foo.<Object> foo
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Object>)Object
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
实例化签名:完全实例化的签名。这是此步骤的结果(此时不会对 的签名进行更多的类型推断)。
目标类型:进行调用的上下文。如果方法调用是赋值的一部分,它将是左侧。如果方法调用本身是方法调用的一部分,则它将是参数类型。foo
foo
由于您的方法调用是悬空的,因此没有目标类型。由于没有目标类型,因此无法进行更多的推断,并且推断为 。foo
T
Object
分析
编译器在推理期间不使用隐式类型的 lambda。在某种程度上,这是有道理的。通常,给定 ,在拥有 的类型之前,您将无法编译。如果您确实尝试推断 from 的类型,则可能会导致先有鸡还是先有蛋的类型问题。在Java的未来版本中可能会对此进行一些改进。param -> BODY
BODY
param
param
BODY
解决 方案
Foo.<Boolean> foo(value -> true)
此解决方案提供了一个显式类型参数(请注意以下部分)。这会将方法签名的部分实例化更改为 ,这是您想要的。foo
with type-args
(Bar<Boolean>)Boolean
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.<Boolean> foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: <none>
with type-args: Boolean
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.<Boolean> foo(value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Foo.foo((Value<Boolean> value) -> true)
此解决方案显式键入您的 lambda,使其与适用性相关(注意如下)。这会将方法签名的部分实例化更改为 ,这是您想要的。with actuals
(Bar<Boolean>)Boolean
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: Bar<Boolean>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo((Value<Boolean> value) -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Foo.foo((Bar<Boolean>) value -> true)
同上,但味道略有不同。
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: Bar<Boolean>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Boolean>)Boolean)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo((Bar<Boolean>) value -> true).booleanValue(); // Compile error here
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
Boolean b = Foo.foo(value -> true)
此解决方案为方法调用提供显式目标(见下文)。这允许延迟实例化推断类型参数应为而不是(见下文)。target-type
Boolean
Object
instantiated signature
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Boolean b = Foo.foo(value -> true);
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Boolean b = Foo.foo(value -> true);
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: Boolean
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
免責聲明
这是正在发生的行为。我不知道这是否是JLS中指定的内容。我可以四处挖掘,看看我是否可以找到指定此行为的确切部分,但是类型推断符号让我头疼。
这也不能完全解释为什么更改为使用 raw 可以解决此问题:Bar
Value
LambdaInference.java:16: Note: resolving method foo in type Foo to candidate 0
Foo.foo(value -> true).booleanValue();
^
phase: BASIC
with actuals: <none>
with type-args: no arguments
candidates:
#0 applicable method found: <T>foo(Bar<T>)
(partially instantiated to: (Bar<Object>)Object)
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: Deferred instantiation of method <T>foo(Bar<T>)
Foo.foo(value -> true).booleanValue();
^
instantiated signature: (Bar<Boolean>)Boolean
target-type: <none>
where T is a type-variable:
T extends Object declared in method <T>foo(Bar<T>)
LambdaInference.java:16: Note: resolving method booleanValue in type Boolean to candidate 0
Foo.foo(value -> true).booleanValue();
^
phase: BASIC
with actuals: no arguments
with type-args: no arguments
candidates:
#0 applicable method found: booleanValue()
出于某种原因,将其更改为使用 raw 允许延迟实例化推断为 。如果我必须推测,我会猜测当编译器试图将lambda拟合到时,它可以通过查看lambda的主体来推断。这意味着我之前的分析是不正确的。编译器可以对 lambda 的主体执行类型推断,但只能对仅出现在返回类型中的类型变量执行类型推断。Value
T
Boolean
Bar<T>
T
Boolean