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

Состав ссылки на метод

Это связано с этим вопросом: Как выполнять композицию функций?

Я заметил, что ссылка на метод может быть назначена переменной, объявленной как Function, и поэтому я предполагаю, что она должна иметь функцию andThen или compose, и, следовательно, я ожидаю, что мы сможем составить их напрямую. Но, видимо, нам нужно назначить их переменной, объявленной как Function first (или типу-лить перед вызовом), прежде чем мы сможем называть andThen или compose на них.

Я подозреваю, что у меня может быть неправильное представление о том, как это должно работать.

Итак, мои вопросы:

  • Зачем нам нужно сначала вводить или назначать его переменной, прежде чем мы сможем вызвать метод andThen?
  • Каков именно тип ссылки на метод, который нужно сделать таким образом?

Пример кода ниже.

public class MyMethods{
    public static Integer triple(Integer a){return 3*a;}
    public static Integer quadruple(Integer a){return 4*a;}

    public int operate(int num, Function<Integer, Integer> f){
        return f.apply(num);
    }

    public static void main(String[] args){
        MyMethods methods = new MyMethods();
        int three = methods.operate(1, MyMethods::triple); // This is fine
        // Error below
        // int twelve = methods.operate(1, (MyMethods::triple).andThen(MyMethods::quadruple));
        // But this one is fine
        Function<Integer, Integer> triple = MyMethods::triple;
        Function<Integer, Integer> quadruple = MyMethods::quadruple;
        int twelve = methods.operate(1, triple.andThen(quadruple));
        // This one is also fine
        int twelve2 = methods.operate(1, ((Function<Integer, Integer>)MyMethods::triple).andThen(MyMethods::quadruple));
    }
}


Подробное описание ошибки

В Eclipse он высвечивается с сообщением об ошибке:

Целевой тип этого выражения должен быть функциональным интерфейсом

Eclipse error about functional interface

и в компиляторе Java 8 ошибка:

java8test.java:14: error: method reference not expected here
        int twelve = methods.operate(1, (MyMethods::triple).andThen(MyMethods::quadruple));
                                         ^
1 error

(на самом деле, почему ошибка в Eclipse отличается от ошибки в компиляторе Java 8)

4b9b3361

Ответ 1

Как говорит Брайан Гетц (руководитель проекта по Java lambdas), "Лямбда-выражения не имеют внутреннего типа" (что также относится к ссылкам на методы). Вот почему вам нужно указать (или назначить) тип Function до того, как его методы станут доступными.

Причина, по которой Eclipse показывает разные сообщения об ошибках из JDK-компилятора (javac), заключается в том, что Eclipse использует свой собственный Java-компилятор, называемый ecj, который является совершенно другой программой из javac. Это, Кстати, почему Eclipse может работать на JRE без необходимости полной установки JDK.

Ответ 2

Вы можете уйти без приведения типов или временных переменных, если вы создадите вспомогательный метод static (где все функции являются параметрами, а не приемниками вызова методов):

static <T,V,R> Function<V, R> chain(
    Function<? super V, ? extends T> f1, Function<? super T, R> f2) {

    return f2.compose(f1);
}

Тогда вы можете просто сказать:

int twelve = methods.operate(1, chain(MyMethods::triple, MyMethods::quadruple));

однако, имейте в виду, что ссылки метода привязки таким образом не являются короткими в исходном коде и неэффективны во время выполнения по сравнению с простым выражением лямбда:

int twelve = methods.operate(1, i -> quadruple(triple(i)));

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