0

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!

11
  • Like list.stream().forEach()? Commented Apr 6, 2017 at 19:09
  • Why does it have to be a lambda. Why can't you pass in any kind of function pointer? Commented Apr 6, 2017 at 19:09
  • 2
    Why not? Have you tried an appropriate functional interface type? Like Comsumer<LowLevelClass>? Commented Apr 6, 2017 at 19:10
  • 1
    Your solution looks about right. Where are you stuck? Commented Apr 6, 2017 at 19:17
  • 1
    @xanmcgregor You don't use the return value of your predicate, this looks wrong to me. Commented Apr 6, 2017 at 19:33

1 Answer 1

2

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);
}
Sign up to request clarification or add additional context in comments.

1 Comment

This is the correct answer. Also, I forgot to put the return value. This ultimately led me to Predicated instead of Consumer. In any case, the explanation is spot-on.

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.