Метод неоднозначен при передаче лямбда-выражения в Java - PullRequest
0 голосов
/ 06 декабря 2018

Давайте получим функциональный интерфейс Functional (для краткости я пропустил реализацию и упростил случай):

@FunctionalInterface 
public interface Functional<E> { 

    void perform(E e);

    default <T extends Number> void method(E e, T t)  { }
    default <T extends Number> void method(E e, Function<E, T> function) { }
} 

И простой фрагмент кода:

Functional<String> functional = (string) -> {};
functional.method("string", (string) -> 1);

Почему метод method() неоднозначен, поскольку в качестве параметра передается лямбда?Это должно быть легко различимо.

Eclipse :

Метод method(String, Function<String,Integer>) неоднозначен для типа Functional<String>

Это также воспроизводится на IntelliJIdea .

Выход Javac (благодаря @ AndyTurner ):

Main.java:21: error: reference to method is ambiguous
        functional.method("string", (string) -> 1);
                  ^
  both method <T#1>method(E,T#1) in Functional and method <T#2>method(E,Function<E,T#2>) in Functional match
  where T#1,E,T#2 are type-variables:
    T#1 extends Number declared in method <T#1>method(E,T#1)
    E extends Object declared in interface Functional
    T#2 extends Number declared in method <T#2>method(E,Function<E,T#2>)

и

Main.java:21: error: incompatible types: cannot infer type-variable(s) T
        functional.method("string", (string) -> 1);
                         ^
    (argument mismatch; Number is not a functional interface)
  where T,E are type-variables:
    T extends Number declared in method <T>method(E,T)
    E extends Object declared in interface Functional

Редактировать : Интересный факт.Когда я заменяю default <T extends Number> на <T>, это работает.T, кажется, не может расширяться Number, Throwable и т. Д. *

default <T> void method(E e, T t)  { }
default <T> void method(E e, Function<E, T> function) { }

Редактировать 2 : когда я задаю универсальный тип T дляобъявление интерфейса, оно также работает:

@FunctionalInterface 
public interface Functional<E, T extends Number> { 

    void get(E e);

    default void method(E e, Function<E, T> function) { }
    default void method(E e, T t)  { }
} 

1 Ответ

0 голосов
/ 06 декабря 2018

Существует несколько билетов ( здесь , здесь и здесь ), которые содержат похожие фрагменты кода.Эти билеты разрешены как «Не проблема», и объяснение таково:

JLS 15.12.2.1:

Выражение потенциально совместимо сцелевой тип в соответствии со следующими правилами:

  • [...]
  • Лямбда-выражение или выражение ссылки на метод потенциально совместимо с переменной типа, если переменная типа является типомпараметр метода-кандидата.

Итак, оба метода method потенциально совместимы в этом случае.

Кроме того, лямбда (string) -> 1 не имеет отношения к применимостипотому что:

JLS 15.2.2.2:

Выражение аргумента считается применимым для потенциально применимого метода m, если только он не имеетследующие формы

  • [...]
  • Если m является универсальным методом, и при вызове метода не предоставляются явные аргументы типа, явно выраженное лямбда-выражение или точноеметоd ссылочное выражение, для которого соответствующий целевой тип (полученный из сигнатуры m) является параметром типа m.

Наконец:

Так как method имеет параметр типа, в котором лямбда передается в качестве аргумента, лямбда пропускается из проверки применимости - то есть оба они применимы - отсюда и неоднозначность.

Возможный обходной путь - приведитеаргумент при вызове метода:

functional.method("string", (Function<String, Number>) (string) -> 1);
...