8

I don't understand how to use lambdas to pass a method as a parameter.

Considering the following (not compiling) code, how can I complete it to get it work ?

public class DumbTest {
    public class Stuff {
        public String getA() {
            return "a";
        }
        public String getB() {
            return "b";
        }
    }

    public String methodToPassA(Stuff stuff) {
        return stuff.getA();
    }

    public String methodToPassB(Stuff stuff) {
        return stuff.getB();
    }

    //MethodParameter is purely used to be comprehensive, nothing else...
    public void operateListWith(List<Stuff> listStuff, MethodParameter method) {
        for (Stuff stuff : listStuff) {
            System.out.println(method(stuff));
        }
    }

    public DumbTest() {
        List<Stuff> listStuff = new ArrayList<>();
        listStuff.add(new Stuff());
        listStuff.add(new Stuff());

        operateListWith(listStuff, methodToPassA);
        operateListWith(listStuff, methodToPassB);
    }

    public static void main(String[] args) {
        DumbTest l = new DumbTest();

    }
}
2
  • Lambdas only work with functional interfaces (an interface with only 1 method, like Runnable). I do not see you declaring a functional interface. Also, when you get an error, tell us what error you are getting and where it's at. Commented Aug 27, 2015 at 17:21
  • I'm not decalring a functional interface because i'm not understanding it when i wrote this post.. this code is not compiling, it must be read as pseudo code for some parts Commented Aug 28, 2015 at 7:33

4 Answers 4

6

Declare your method to accept a parameter of an existing functional interface type which matches your method signature:

public void operateListWith(List<Stuff> listStuff, Function<Stuff, String> method) {
    for (Stuff stuff : listStuff) {
        System.out.println(method.apply(stuff));
    }
}

and call it as such:

operateListWith(listStuff, this::methodToPassA);

As a further insight, you don't need the indirection of methodToPassA:

operateListWith(listStuff, Stuff::getA);
Sign up to request clarification or add additional context in comments.

5 Comments

Why would you do this? Why wouldn't you just do listStuff.stream().map(method).forEach(System.out::println)? Isn't that the entire point of lambdas?
I actually prefer OP's way to learning: separating the lambda (a language feature) form Streams (an API which uses it). Lambdas are a far wider story than just the Streams API. Yes, in production code, one would write it your way.
I'm not using stream because i'd like to understand how to pass a method as a parameter, as Marko guessed it
This does not work like this ; i get "The method apply(String) in the type Function<String,String> is not applicable for the arguments (DumbTest.Stuff)" compilation error
Yes, you need <Stuff, String> in fact. Fixed.
3

Your MethodParameter should be some interface you define with a single method. This is referred to as a functional interface. You can then pass your methods in. A quick demonstration:

public interface Test{
    void methodToPass(string stuff);
}

[...]

public class DumbTest{
     //MethodParameter is purely used to be comprehensive, nothing else...
    public void operateListWith(List<Stuff> listStuff, Test method) {
        for (Stuff stuff : listStuff) {
            System.out.println(method(stuff));
        }
    }
    public DumbTest() {
        List<Stuff> listStuff = new ArrayList<>();
        //fill list
        operateListWith(listStuff, methodToPassA);
        operateListWith(listStuff, methodToPassB);
    }
}

Comments

2

The definition of MethodParameter is missing from your source code. To be used with lambda expressions, it must be a functional interface, for example:

@FunctionalInterface
interface MethodParameter {
    String apply(Stuff input);
}

(The @FunctionalInterface annotation is optional.)

To use the method, you have call the method from the interface:

System.out.println(method.apply(stuff));

And thirdly, a method reference always needs a context. In your case you have to do:

operateListWith(listStuff, this::methodToPassA);
operateListWith(listStuff, this::methodToPassB);

Comments

1

You need to use method references.

You don't need to create a method like operateListWith, that's sort of the whole idea. Instead, you can operate on each value using forEach by doing something like this:

listStuff.stream.forEach(object::methodToPassA);

For example:

public class StreamExample {
  public static void main(String[] args) {
    List<String> list = Arrays.asList("Hello", "What's Up?", "GoodBye");
    list.stream().forEach(System.out::println);
  }
}

Output:

Hello
What's Up?
GoodBye

In your case, you can get the value inside Stuff using .map, and then operate on it using forEach, like this:

public class DumbTest {
  public class Stuff {
    public String getA() {
      return "a";
    }

    public String getB() {
      return "b";
    }
  }

  public String methodToPassA(Stuff stuff) {
    return stuff.getA();
  }

  public String methodToPassB(Stuff stuff) {
    return stuff.getA();
  }

  public DumbTest() {
    List<Stuff> listStuff = Arrays.asList(new Stuff(), new Stuff());

    listStuff.stream()
        .map(this::methodToPassA)
        .forEach(System.out::println);
  }

  public static void main(String[] args) {
    DumbTest l = new DumbTest();
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.