方法参考的组成

这与这个问题有关:如何做功能组合?

我注意到方法引用可以分配给声明为 的变量,因此我假设它应该具有或功能,因此我希望我们可以直接组合它们。但显然,我们需要先将它们分配给声明为first(或在调用之前进行类型转换)的变量,然后才能调用或调用它们。FunctionandThencomposeFunctionandThencompose

我怀疑我可能对这应该如何工作有一些误解。

所以我的问题:

  1. 为什么我们需要先进行类型转换或将其分配给变量,然后才能调用该方法?andThen
  2. 以这种方式需要完成的方法引用的确切类型是什么?

下面的示例代码。

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

正如Brian Goetz(Java lambdas的项目负责人)所说,“Lambda表达式没有内在类型”(这也适用于方法引用)。这就是为什么您需要强制转换(或分配)类型,然后它的方法才可用。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));

但是,请记住,与简单的 lambda 表达式相比,以这种方式链接方法引用在源代码中既不短,在运行时也不更有效:

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

请注意,最后一个解决方案不需要类型转换、其他变量或帮助器方法。如果您有一个已经存在的方法,该方法适合需要函数的位置,但是从多个方法引用中组合函数并不是很有用(在大多数情况下),则方法引用是一个很好的工具。


推荐