Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Java lambdas bind methods to their parameters?

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

    }
}
like image 567
bigcodeszzer Avatar asked Jul 20 '16 18:07

bigcodeszzer


2 Answers

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
    }
}
like image 98
Holger Avatar answered Sep 25 '22 02:09

Holger


Looking at the code behind the second link:

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

The Java equivalent would be:

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

Where:

  • distribution is a java.util.function.Function.
  • ... is the return type of the call.

For different combinations of in- and output types, there are different functional interfaces available in java.util.function. You can also define you're own functional interface.

A lambda can be assigned to a variable with a functional interface type, as long as it matches the in- and output types defined by the interface's method.


Besides the lack of operator overloading and an auto equivalent, in Java. The types of the captured variables are all ways either reference types (all objects), or primitives.

You can change a captured reference type variable by calling (non-const, there is no such thing in Java) methods on it. Like apply is called on distribution in the example.

All captured variables must be final, or effectively final. That means you can not assign new values to the captured variables. I.e. distribution = someVal, is not allowed, for both reference and primitive types.

like image 27
Jorn Vernee Avatar answered Sep 25 '22 02:09

Jorn Vernee