Итак, перегрузка методов — это плохо™. Теперь, когда это решено, давайте предположим, что я действительно хочу перегрузить такой метод:
static void run(Consumer<Integer> consumer) {
System.out.println("consumer");
}
static void run(Function<Integer, Integer> function) {
System.out.println("function");
}
В Java 7 я мог бы легко вызывать их с недвусмысленными анонимными классами в качестве аргументов:
run(new Consumer<Integer>() {
public void accept(Integer integer) {}
});
run(new Function<Integer, Integer>() {
public Integer apply(Integer o) { return 1; }
});
Теперь, в Java 8, я, конечно, хотел бы вызывать эти методы с помощью лямбда-выражений, и я могу это сделать!
// Consumer
run((Integer i) -> {});
// Function
run((Integer i) -> 1);
Поскольку компилятор должен иметь возможность вывести Integer
, почему бы тогда не оставить Integer
?
// Consumer
run(i -> {});
// Function
run(i -> 1);
Но это не компилируется. Компилятору (javac, jdk1.8.0_05) это не нравится:
Test.java:63: error: reference to run is ambiguous
run(i -> {});
^
both method run(Consumer<Integer>) in Test and
method run(Function<Integer,Integer>) in Test match
Для меня интуитивно это не имеет смысла. Между лямбда-выражением, которое возвращает возвращаемое значение (совместимо со значением), и лямбда-выражением, которое дает void
(совместимо с void), как указано в документе JLS §15.27.
Но, конечно же, JLS глубок и сложен, и мы унаследовали 20-летнюю историю обратной совместимости, и есть новые вещи, такие как:
Некоторые выражения аргументов, содержащие лямбда-выражения с неявным типом (§15.27.1) или неточные ссылки на методы (§15.13.1) игнорируются тестами применимости, поскольку их значение не может быть определено до тех пор, пока не будет задан целевой тип. выбран.
Указанное выше ограничение, вероятно, связано с тем, что JEP 101 не был полностью реализован, т.к. можно увидеть здесь и здесь.
Вопрос:
Кто может мне точно сказать, какие части JLS указывают на эту двусмысленность во время компиляции (или это ошибка компилятора)?
Бонус: почему все решилось именно так?
Обновлять:
С jdk1.8.0_40 вышеприведенное компилируется и работает нормально
i
— это первый (и единственный) аргумент либоConsumer.accept()
, либоFunction.apply()
. Это само по себе может быть двусмысленным. Но учитывая, что одна лямбда оценивается как тип, совместимый со значением (Function
), а другая оценивается как тип, совместимый с void (Consumer
), я интуитивно думаю, что двусмысленности нет. - person Lukas Eder   schedule 02.05.2014beta 102
и ранее). - person Holger   schedule 02.05.2014run((Integer i) -> {})
является Потребителем. Таким образом, несмотря на то, что это может быть как Function‹Integer,Integer›, так и Consumer‹Integer›, Consumer‹Integer› лучше всего подходит, и компилятор использует его. Вопрос в том, почему компилятор делает это только тогда, когда вы указываете(Integer i)
, а не толькоi
. - person jacobhyphenated   schedule 02.05.2014i -> {}
никогда не может оцениваться какFunction
, потому что он совместим с void.i -> 1
никогда не может быть оценено какConsumer
, потому что оно совместимо по значению. На мой взгляд, для каждого вызова даже применим только один из перегруженных методов. Как также указал @jacobhyphenated, двусмысленность можно устранить, явно указав идентичные типы аргументов функции(Integer i)
. - person Lukas Eder   schedule 02.05.2014run((Consumer<Integer>) (Integer i) -> {1});
, он не скомпилируется. Это наводит меня на мысль, что это должна быть ошибка компилятора, поскольку между двумя лямбда-выражениями действительно нет двусмысленности. - person jacobhyphenated   schedule 02.05.2014i->i.thing()
может бытьvoid
или значением, мы не знаем, пока не знаем, что такоеi
. Кажется, что компилятор не хочет рассуждать об этом, несмотря на то, что обе лямбды имеютInteger
параметра. - person ggovan   schedule 02.05.2014i -> 1
иi -> {}
), тогда как лямбда-выражения вообще могут быть неоднозначными (i -> intFunction()
иi -> voidFunction()
). Теперь докажи это, и я приму твой ответ :-) - person Lukas Eder   schedule 02.05.2014