Состав ссылки на метод
Это связано с этим вопросом: Как выполнять композицию функций?
Я заметил, что ссылка на метод может быть назначена переменной, объявленной как 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)
Ответы
Ответ 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)));
Обратите внимание, как последнее решение не требует применения типов, дополнительных переменных или вспомогательных методов. Ссылки на методы являются хорошим инструментом, если у вас уже есть существующий метод, который подходит там, где требуется функция, но составление функции из нескольких ссылок на методы не очень полезно (в большинстве случаев).