Ответ 1
Под капюшоном
Используя некоторые скрытые функции 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>)
phase: применимость метода
actuals: фактические аргументы, переданные в
type-args: аргументы явного типа
кандидаты: потенциально применимые методы
actuals <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
).
target-type: контекст, в который делается вызов. Если вызов метода является частью назначения, он будет левым. Если вызов метода сам по себе является частью вызова метода, это будет тип параметра.
Поскольку ваш вызов метода свисает, нет целевого типа. Поскольку нет целевого типа, больше не может быть сделано вывод на foo
, а T
- Object
.
Анализ
Компилятор не использует неявно типизированные lambdas во время вывода. В определенной степени это имеет смысл. В общем случае, учитывая param -> BODY
, вы не сможете скомпилировать BODY
, пока не получите тип для param
. Если вы попытались вывести тип param
из BODY
, это может привести к проблеме типа курица и яйцо. Возможно, некоторые улучшения будут сделаны в этом в будущих выпусках Java.
Решение
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)
Это решение явно вводит вашу лямбду, что позволяет ей быть применимой к применимости (примечание 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. Я мог бы копаться и посмотреть, могу ли я найти точный раздел, который определяет это поведение, но тип ввода обозначает головную боль.
Это также не полностью объясняет, почему изменение Bar
для использования raw 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 Value
позволяет отложенному экземпляру сделать вывод, что T
- Boolean
. Если бы мне пришлось размышлять, я бы предположил, что, когда компилятор пытается привязать лямбда к Bar<T>
, он может сделать вывод, что T
есть Boolean
, просматривая тело лямбда. Это означает, что мой предыдущий анализ неверен. Компилятор может выполнять вывод типа в теле лямбда, но только для переменных типа, которые появляются только в возвращаемом типе.