11

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

    }
}
Community
  • 1
  • 1
bigcodeszzer
  • 916
  • 1
  • 8
  • 27
  • Have you tried doing this? Did you run into any trouble? – Jorn Vernee Jul 20 '16 at 18:13
  • 1
    I've done it in C++. I wouldn't know where to begin Java, and can't find any documentation, here or elsewhere. – bigcodeszzer Jul 20 '16 at 18:14
  • Being able to pass actual functions in c++ is different than lambdas. In java there is no implementation to pass a function from a class. I hope that answers it i'm unsure if that's what you are looking for. – Javant Jul 20 '16 at 18:19
  • 2
    @Javant That's not entirely true, there is a thing called method references. – Jorn Vernee Jul 20 '16 at 18:19
  • Could you post a short example of code doing what you want so Java experts can provide the Java analog (if any)? Because as it is, it's hard to say what *exactly* are you asking. – Sergei Tachenov Jul 20 '16 at 18:24
  • @SergeyTachenov Working on it.... – bigcodeszzer Jul 20 '16 at 18:28
  • edited.... added a code example – bigcodeszzer Jul 20 '16 at 18:45
  • As a note, I would say that, to really understand lambdas in Java, you must first understand anonymous classes. Since lambdas are a shorthand for anonymous classes that implement an interface with only 1 method. – Jorn Vernee Jul 20 '16 at 21:55
  • @JornVernee I gave that a shot in the edit. Does that look correct? – bigcodeszzer Jul 20 '16 at 22:25
  • I'm trying to think of a way to wrap it, to make it prettier. But I think it's impossible. Hence, lambdas. – bigcodeszzer Jul 20 '16 at 22:29
  • Not sure, but ... are you looking for the same thing that I asked for in http://stackoverflow.com/questions/36336848/is-there-any-way-to-circumvent-the-typedness-of-lambda-expressions ?! – Marco13 Jul 20 '16 at 23:11

3 Answers3

8

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
    }
}
Holger
  • 285,553
  • 42
  • 434
  • 765
1

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.

Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93
1

In your example, you could do this:

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

I've used Runnable because it's a ready out-of-the-box thing. But if you have some sort of complicated signature, you can easily declare a functional interface: that is, an interface with just one method (doesn't matter what the method is called if you're going to use lambdas). Then iter would accept a parameter of that interface type, and the lambdas will generally look like (par1, par2) -> callYourFunction(par1, someValue, par2, someOtherValue). Those “values” are kind of “bound”, that is, copied much like you'd do with [=] in C++. But that's only as far as local variables are concerned (and they have to be effectively final anyway, as @Jorn mentions). But you can access fields of the enclosing class absolutely freely (meaning you can even change them). That's technically because this is captured by value, just like locals, and therefore you can modify the object this points to.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • So you can use an interface to provide a type for a lambda? Could you use a class to do the same thing? I'm kind of confused as to how the runnable interface is calling the mybind through run()? – bigcodeszzer Jul 20 '16 at 19:30
  • How does the runnable interface = the lambda? Does it invoke an implicit constructor, or store it somehow? – bigcodeszzer Jul 20 '16 at 19:40