Java lambda可以将方法绑定到它们的参数吗?

时间:2016-07-20 18:05:54

标签: java lambda function-pointers function-binding

这里讨论如何使用lambdas将方法作为参数传递:
Java Pass Method as Parameter

在其他语言中,即C ++,可以使用Lambdas将函数绑定到它的参数 - 在此讨论:
Bind Vs Lambda?

在Java中,是否可以使用lambdas绑定方法?

如果是这样,你会如何做到这一点?

修改>>>>

根据要求,我通常尝试做的一个例子:

警告,这里有伪代码。

public class DataView {

    private static ArrayList<Float> rectData = new ArrayList<Float>();
    private static ArrayList<Float> textData = new ArrayList<Float>();

    DataView(){

        //Pseudo Code:
        boundFunction mybind  = boundFunction(functionA, 5, 10);
        boundFunction mybind2 = boundFunction(functionB, 10, 12);

        iter(mybind);
        iter(mybind2);

    }

    //Method with pseudo parameter
    private void iter(functionSignature){
        for(Float i : textData){

            //Pseudo call to pseudo parameter
            functionSignature();

        }
    }

    private void functionA(int a, int b){
        //dostuff

    }

    private void functionB(int a, int b){
        //do other stuff

    }

}

请记住,我不是在寻找'另一种实现此功能的方法' - 这个例子是为了说明我希望将函数用作参数以及将参数绑定到这些函数的一般方法。 / p>

编辑&gt;&GT;&GT;

尝试使用匿名类:

public class DataView {

    private class Bound{ 
        public void run(){}

    }

    private static ArrayList<Float> rectData = new ArrayList<Float>();
    private static ArrayList<Float> textData = new ArrayList<Float>();

    DataView(){

        Bound mybind = new Bound(){
            public void run(){
                functionA(5,10);
            }
        };

        Bound mybind2 = new Bound(){
            public void run(){
                functionB(5,10);
            }
        };

        iter(mybind);
        iter(mybind2);

    }

    private void iter(Bound function){
        for(Float i : textData){

            function.run();

        }
    }

    private void functionA(int a, int b){
        //dostuff

    }

    private void functionB(int a, int b){
        //do other stuff

    }
}

3 个答案:

答案 0 :(得分:6)

正如其他答案所述,Java需要一个实际的功能接口类型来表示一个函数。这也适用于绑定操作,甚至需要两个这样的接口,一个用于表示未绑定的函数,另一个用于绑定函数:

public class DataView {
    interface NoArgFunction {
        void func();
    }
    interface TwoIntFunction {
        void func(int a, int b);
    }
    static NoArgFunction bind(TwoIntFunction f, int first, int second) {
        return () -> f.func(first, second);
    }

    private static ArrayList<Float> rectData = new ArrayList<Float>();
    private static ArrayList<Float> textData = new ArrayList<Float>();

    DataView(){
        NoArgFunction mybind  = bind(this::functionA, 5, 10);
        NoArgFunction mybind2 = bind(this::functionB, 10, 12);

        iter(mybind);
        iter(mybind2);
    }
    private void iter(NoArgFunction noArg){
        for(Float i : textData){
            noArg.func();
        }
    }
    private void functionA(int a, int b){
        //dostuff
    }
    private void functionB(int a, int b){
        //do other stuff
    }
}

作为一个有趣的事实,在字节码级别上,有一种方法可以将方法与参数组合而不需要中间步骤,这正是lambda表达式() -> f.func(first, second)所使用的,它被编译成一个保持lambda主体并具有firstsecond两个参数的合成方法,它将在运行时用于构造绑定NoArgFunction和{{的当前值的first实例1}}。

但是你不能在Java源代码中使用它。现有方法唯一受支持的绑定是在方法引用secondthis::functionA中看到的绑定,它们都将当前this::functionB实例绑定到方法this和{{1} }。好吧,使用像lamb这样的lambda表达式 functionA首先没有functionB方法会缩短整个过程......

所以最重要的是,试图与试图模拟非内在支持的功能的语言进行斗争没有多大意义。使用lambda表达式而不是绑定,以及为此目的预定义的目标类型使代码更加简单:

NoArgFunction mybind = () -> functionA(5, 10);

答案 1 :(得分:1)

查看第二个链接背后的代码:

auto dice = [&]() { return distribution(engine); };

Java等价物将是:

Supplier<...> dice = () -> distribution.apply(engine);

其中:

  • distributionjava.util.function.Function
  • ...是通话的返回类型。

对于输入和输出类型的不同组合,java.util.function中提供了不同的功能接口。您还可以定义自己的功能界面。

lambda可以分配给具有功能接口类型的变量,只要它匹配接口方法定义的输入和输出类型。

除了缺少运算符重载和auto等效之外,还有Java。捕获变量的类型可以是引用类型(所有对象)或基元。

您可以通过调用(非constthere is no such thing in Java)方法更改捕获的引用类型变量。就像示例中的apply上调用了distribution一样。

所有捕获的变量必须为final,或有效最终。这意味着您无法为捕获的变量分配新值。即对于引用和基本类型,不允许distribution = someVal

答案 2 :(得分:1)

在您的示例中,您可以执行此操作:

public class DataView {

    private static ArrayList<Float> textData = new ArrayList<Float>();

    DataView(){
        Runnable mybind  = () -> functionA(5, 10);
        Runnable mybind2 = () -> functionB(10, 12);

        iter(mybind);
        iter(mybind2);

    }

    //Method with pseudo parameter
    private void iter(Runnable r){
        for(Float i : textData){
            r.run();
        }
    }

    private void functionA(int a, int b){
        //dostuff
    }

    private void functionB(int a, int b){
        //do other stuff
    }
}

我已经使用了Runnable,因为它是一个开箱即用的东西。但是如果你有某种复杂的签名,你可以很容易地声明一个功能界面:也就是说,只有一种方法的界面(如果你&#39,那么调用方法是什么并不重要;重新使用lambdas)。然后iter将接受该接口类型的参数,lambda通常看起来像(par1, par2) -> callYourFunction(par1, someValue, par2, someOtherValue)。那些“价值”有点“束缚”,就像你在C ++中用[=]那样复制。但这只涉及局部变量(并且无论如何它们必须是有效的最终,正如@Jorn所提到的那样)。但是你可以完全自由地访问封闭类的 fields (这意味着你甚至可以更改它们)。这在技术上是因为this是按值捕获的,就像本地人一样,因此您可以修改对象this指向。