xanmcgregor
xanmcgregor

Reputation: 382

Java Lambdas: Sending method name as parameter

I've run into a problem in which my class contains several methods with a lot of duplicated code. The reason behind this is that each method traverses a list of entries and calls specific entry method.

In code...

The LowLevelClass class has the following structure:

public class LowLevelClass {

    // constructor omitted

    public boolean doSomethingA() {
        // some non-duplicated code
        return true;
    }

    public boolean doSomethingB() {
        // some non-duplicated code
        return true;
    }

    public boolean doSomethingC() {
        // some non-duplicated code
        return true;
    }
}

The top level class contains a List of LowLevelClasses and has the same number of methods, but this time, with a lot of duplications:

public class HighLevelClass {

    private List<LowLevelClass> classes = new ArrayList<>();

    public HighLevelClass() {
        this.classes.add(new LowLevelClass(/* params */));
        this.classes.add(new LowLevelClass(/* params */));
        this.classes.add(new LowLevelClass(/* params */));
    }

    public void doA() {
        System.out.println("Doing ...");
        for (LowLevelClass entry : classes) {
            System.out.println("Doing something...");
            entry.doSomethingA();
            System.out.println("Done");
        }
    }

    public void doB() {
        System.out.println("Doing ...");
        for (LowLevelClass entry : classes) {
            System.out.println("Doing something...");
            entry.doSomethingB();
            System.out.println("Done");
        }
    }

    public void doC() {
        System.out.println("Doing ...");
        for (LowLevelClass entry : classes) {
            System.out.println("Doing something...");
            entry.doSomethingC();
            System.out.println("Done");
        }
    }
}

My goal is to have something in form of:

public class HighLevelClass {

    private List<LowLevelClass> classes = new ArrayList<>();

    public HighLevelClass() {
        this.classes.add(new LowLevelClass());
        this.classes.add(new LowLevelClass());
        this.classes.add(new LowLevelClass());
    }

    public void doSomething(Lambda /* Functional interface*/ operation) {
        System.out.println("Doing A");
        for (LowLevelClass entry : classes) {
            System.out.println("Doing something...");
            entry.operation; // or something else...
            System.out.println("Done");
        }
    }

    public void doSomethingA() {
        // my goal... and maybe in totally wrong direction is to send something in form of...
        return doSomething(LowLevelClass::doSomethingA);
    }

    // etc
}

Can this be done in Java 8 with Lambdas? In other words, can I define the method to perform on each entry of the given list?

EDIT 1

The answers provided by Jorn Vernee and Joffrey are correct!

Ultimately, the solution was to use Predicate. (see EDIT 2 why I didn't use Consumer in the end...)

public class HighLevelClass {

private List<LowLevelClass> classes = new ArrayList<>();

public HighLevelClass() {
    this.classes.add(new LowLevelClass());
    this.classes.add(new LowLevelClass());
    this.classes.add(new LowLevelClass());
}

public boolean doSomething(Predicate<LowLevelClass> function) {
    System.out.println("Doing A");
    for (LowLevelClass entry : classes) {
        System.out.println("Doing something...");
        boolean val = function.test(entry);
        System.out.println("Done " + val);
    }
    return someEndVerdict; 
}

public boolean doSomethingA() {
    return doSomething(LowLevelClass::doSomethingA);
}

// etc

}

EDIT 2

My initial methods in HighLevelClass didn't contain boolean return type. That's the reason why I used Predicate (Predicate, as a contast to Consumer, returns boolean value which suited me better - and which I forgot to initially mention :((( )

Thanks for help and time!

Upvotes: 0

Views: 89

Answers (1)

Joffrey
Joffrey

Reputation: 37829

You should not confuse the way you call a method, which may or may not involve a lambda, and the way you write a method, which involves finding the right argument types.

When you write a method, you need to focus on your arguments' types. If one of them is an object representing a function, what you need is to understand the appropriate signature that this function should match, and this will give you the functional interface you should put as type of your param.

In your case, you expect a function that takes 1 argument of type LowLevelClass and returns no value. You might be surprised by that, but you need to think of instance methods as functions that take an instance of the class (this) as an extra first argument (as opposed to static methods).

Therefore, the Consumer<LowLevelClass> interface is what you want:

public void doSomething(Consumer<LowLevelClass> operation) {
    System.out.println("Doing A");
    for (LowLevelClass entry : classes) {
        System.out.println("Doing something...");
        operation.accept(entry); // or something else...
        System.out.println("Done");
    }
}

public void doSomethingA() {
    return doSomething(LowLevelClass::doSomethingA);
}

Upvotes: 2

Related Questions