Can Java lambdas bind methods to their parameters?

bigcodeszzer picture bigcodeszzer · Jul 20, 2016 · Viewed 9.5k times · Source

How to pass a method as a parameter using lambdas is discussed here:
Java Pass Method as Parameter

In other languages, namely C++, it is possible to bind a function to it's parameters using Lambdas - discussed here:
Bind Vs Lambda?

Is it possible, in Java, to bind a method using lambdas?

If so, how would you accomplish this?

Edit >>>>

An example, by request, of what I am generally trying to do:

Be warned, there is pseudo code here.

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

    }

}

Bare in mind, I'm not looking for 'another way to accomplish this functionality' - this example is to illustrate a general way in which I would like to use functions as parameters, and to bind parameters to those functions.

Edit>>>

Attempt using anonymous classes:

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

    }
}

Answer

Holger picture Holger · Jul 21, 2016

As said in other answers, Java requires an actual functional interface type to represent a function. This also applies to a bind operation which requires even two of such interfaces, one to represent the unbound function and one for the bound function:

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
    }
}

As a fun fact, on the bytecode level, there is a capability to combine a method with arguments without the intermediate step, which is exactly what is being used by the lambda expression () -> f.func(first, second), which gets compiled into a synthetic method holding the lambda body and having two parameters for first and second which will be used at runtime to construct the NoArgFunction instance binding the current values of first and second.

But you can’t use this in Java source code. The only supported binding for existing methods is the one seen at the method references this::functionA and this::functionB, both binding the current this instance to the methods functionA and functionB. Well, using a lambda expression like
NoArgFunction mybind = () -> functionA(5, 10); in the first place without a bind method does short-cut the process…

So the bottom line is that there’s not much sense in trying to fight against the language trying to model a feature that isn’t intrinsically supported. Using a lambda expression instead of binding, together with a target type predefined for this purpose makes the code much simpler:

public class DataView {
    private static ArrayList<Float> rectData = new ArrayList<Float>();
    private static ArrayList<Float> textData = new ArrayList<Float>();

    DataView(){
        iter(x -> functionA(5, 10));
        iter(x -> functionB(10, 12));
    }
    private void iter(Consumer<Float> function){
        textData.forEach(function);
    }
    private void functionA(int a, int b){
        //dostuff
    }
    private void functionB(int a, int b){
        //do other stuff
    }
}