Подтвердить что ты не робот

Ссылка на метод неоднозначна при использовании лямбда и дженериков

Я получаю сообщение об ошибке в следующем коде, который, я считаю, не должен быть там... Использование JDK 8u40 для компиляции этого кода.

public class Ambiguous {
    public static void main(String[] args) {
        consumerIntFunctionTest(data -> {
            Arrays.sort(data);
        }, int[]::new);

        consumerIntFunctionTest(Arrays::sort, int[]::new);
    }

    private static <T> void consumerIntFunctionTest(final Consumer<T> consumer, final IntFunction<T> generator) {

    }

    private static <T> void consumerIntFunctionTest(final Function<T, ?> consumer, final IntFunction<T> generator) {

    }
}

Ошибка следующая:

Ошибка: (17, 9) java: ссылка на consumerIntFunctionTest неоднозначна   оба метода consumerIntFunctionTest (java.util.function.Consumer, java.util.function.IntFunction) в net.tuis.ubench.Abiguous и метод consumerIntFunctionTest (java.util.function.Function, java.util.function.IntFunction) в сети .tuis.ubench. Незначительное совпадение

Ошибка происходит в следующей строке:

consumerIntFunctionTest(Arrays::sort, int[]::new);

Я считаю, что ошибки не должно быть, поскольку все ссылки Arrays::sort имеют тип void, и ни одно из них не возвращает значение. Как вы можете заметить, он работает, когда я явно расширяю Consumer<T> lambda.

Это действительно ошибка в javac, или же JLS заявляет, что лямбда не может автоматически расширяться в этом случае? Если это последний, я все равно думаю, что это странно, поскольку consumerIntFunctionTest с первым аргументом Function<T, ?> не должен совпадать.

4b9b3361

Ответ 1

В первом примере

consumerIntFunctionTest(data -> {
        Arrays.sort(data);
    }, int[]::new);

выражение лямбда имеет void -собираемый блок, который может быть идентифицирован структурой выражения без необходимости разрешать фактические типы.

Напротив, в примере

consumerIntFunctionTest(Arrays::sort, int[]::new);

ссылка на метод должна быть решена, чтобы выяснить, соответствует ли она либо функции void (Consumer), либо функции возврата значения (Function). То же самое относится к упрощенному лямбда-выражению

consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new);

которые могут быть как void - совместимыми, так и совместимыми по стоимости, в зависимости от разрешенного целевого метода.

Проблема заключается в том, что для решения метода требуется знание требуемой сигнатуры, которая должна определяться с помощью целевого ввода, но целевой тип неизвестен до тех пор, пока не будут известны параметры типа общего метода. В то время как теоретически оба могут быть определены сразу, процесс (все еще ужасно сложный) был упрощен в спецификации в этом методе, когда сначала выполняется перегрузка, и применяется процедура ввода вывода (см. JLS §15.12.2). Следовательно, информация, которую может предоставить вывод типа, не может использоваться для разрешения разрешения перегрузки.

Но обратите внимание, что первый шаг, описанный в 15.12.2.1. Определение потенциально применимых методов содержит:

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

  • Лямбда-выражение (§15.27) потенциально совместимо с функциональным интерфейсом типа (§9.8), если все верно:

    • Арность типа типа целевого типа такая же, как и арность выражения лямбда.

    • Если тип функции целевого типа имеет возврат void, то тело лямбда является выражением оператора (§14.8) или блоком, совместимым с void (§15.27.2).

    • Если тип функции целевого типа имеет (непустой) возвращаемый тип, то тело лямбда является либо выражением, либо совместимым по значению блоком (§15.27.2).

  • Ссылочное выражение метода (§15.13) потенциально совместимо с типом функционального интерфейса, если, когда тип типа типа arty равен n, существует по крайней мере один потенциально применимый метод для ссылочного выражения метода с arity n ( § 15.13.1), и одно из следующего верно:

    • Ссылочное выражение метода имеет форму ReferenceType:: [TypeArguments] Идентификатор и по крайней мере один потенциально применимый метод: i) статический и поддерживает arity n, или ii) не статический и поддерживает arity n-1.

    • В ссылочном выражении метода есть другая форма, и по крайней мере один потенциально применимый метод не является статическим.

...

Определение потенциальной применимости выходит за рамки базовой проверки достоверности, чтобы также учитывать наличие и "форму" целевых типов функционального интерфейса. В некоторых случаях, связанных с выводом аргумента типа, выражение лямбда, которое появляется как аргумент вызова метода, не может быть правильно напечатано до разрешения перегрузки.

Итак, в первом примере один из методов сортируется формой lambdas, а в случае ссылки на метод или лямбда-выражения, состоящего из выражения единственного вызова, оба потенциально применимых метода переносят этот первый процесс выбора и дают "неоднозначная" ошибка до того, как вывод типа может начать поиск целевого метода, чтобы определить, есть ли его метод void или возвращающий значение.

Обратите внимание, что, как и при использовании x->{ foo(); }, чтобы явно выразить лямбда-выражение void -compatible, вы можете использовать x->( foo() ), чтобы сделать лямбда-выражение явно совместимым с оценкой.


Вы, сумасшедший, далее читаете этот ответ, объясняя, что это ограничение суммированного вывода и разрешения перегрузки метода было преднамеренным (но не простым) решением.