问题
This is related to this question: How to do function composition?
I noticed that a method reference can be assigned to a variable declared as Function
, and so I assume it should have andThen
or compose
function, and hence I expect that we can compose them directly. But apparently we need to assign them to a variable declared as Function
first (or type-cast before invocation) before we can call andThen
or compose
on them.
I suspect I might have some misconception about how this should work.
So my questions:
- Why do we need to type-cast or assign it to a variable first before we can call the
andThen
method? - What exactly is the type of method reference that it needs to be done in this way?
Sample code below.
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));
}
}
More description on the error
In Eclipse it's highlighted with the error message:
The target type of this expression must be a functional interface
and in Java 8 compiler the error is:
java8test.java:14: error: method reference not expected here int twelve = methods.operate(1, (MyMethods::triple).andThen(MyMethods::quadruple)); ^ 1 error
(actually, why is the error in Eclipse different from the one from Java 8 compiler?)
回答1:
As Brian Goetz (project lead for Java lambdas) says, "Lambda expressions have no intrinsic type" (which applies to method references too). This is why you need to cast (or assign) to type Function
before its methods become available.
The reason Eclipse shows different error messages from the JDK compiler (javac) is that Eclipse uses its own Java compiler, called ecj, which is a totally different program from javac. This is, BTW, why Eclipse can run on the JRE, without needing a full JDK install.
回答2:
You can get away without type casts or temporary variables if you create a static
helper method (where all functions are parameters rather than method invocation receivers):
static <T,V,R> Function<V, R> chain(
Function<? super V, ? extends T> f1, Function<? super T, R> f2) {
return f2.compose(f1);
}
Then you can simply say:
int twelve = methods.operate(1, chain(MyMethods::triple, MyMethods::quadruple));
however, keep in mind that chaining method references this way is neither shorter in source code nor more efficient at runtime compared to a simple lambda expression:
int twelve = methods.operate(1, i -> quadruple(triple(i)));
Note how the last solution does not require type casts, additional variables nor helper methods. Method references are a good tool if you have an already existing method that fits where a function is required but composing a function out of multiple method references is not really useful (in most cases).
来源:https://stackoverflow.com/questions/27559960/composition-of-method-reference