Why does forEach method accept lambda that invokes method with multiple arguments when Consumer only takes one argument?

amye93 :

I am playing with forEach on a List<String>, and I'm confused about why the following line is acceptable:

policies.forEach(policy -> test.addToDatabase(policy, stats));

Since forEach requires a Consumer, and the Consumer accept method only takes one argument, I don't understand why the call to addtoDatabase is acceptable, as it takes two arguments. See below for full test code. Note that I am only playing around here to learn, so this code is not meant to be perfect or elegant.

public class ConsumerTest {

    private Random random = new Random();

    public static void main(String[] args) {
        ConsumerTest test = new ConsumerTest();
        List<String> policies = new ArrayList<>();
        policies.add("11111");
        policies.add("22222");
        policies.add("33333");
        policies.add("44444");
        policies.add("55555");
        Stats stats = test.new Stats();
        policies.forEach(policy -> test.addToDatabase(policy, stats));
        System.out.println("Success count: " + stats.getSuccessCount() + "\nFailure count: " + stats.getFailureCount());
    }

    private void addToDatabase(String policy, Stats stats) {
        // simulate success/failure adding to DB with Random
        if (random.nextBoolean()) {
            stats.incrementSuccessCount();
            System.out.println("Success for Policy " + policy);
        } else {
            stats.incrementFailureCount();
            System.out.println("Failure for Policy " + policy);
        }
    }

    class Stats {
        private int successCount;
        private int failureCount;
        public void incrementSuccessCount() {
            successCount++;
        }
        public void incrementFailureCount() {
            failureCount++;
        }
        public int getSuccessCount() {
            return successCount;
        }
        public int getFailureCount() {
            return failureCount;
        }
    }
}
Nikolas :

I am playing with forEach on a List of Strings, and I'm confused about why the following line is acceptable:

policies.forEach(policy -> test.addToDatabase(policy, stats));

It is. You confuse the parameter of the Iterable::forEach with the parameters of the statements inside the lambda expression. Since the only parameter inside the Iterable::forEach is Consumer<T> which is nothing than an implementation of the very same anonymous class:

Consumer<String> consumer = new Consumer<>() {
    @Override
    public void accept(final String policy) {
        test.addToDatabase(policy, stats)
    }
};

policies.forEach(consumer);

It is the same as:

Consumer<String> consumer = policy -> test.addToDatabase(policy, stats);
policies.forEach(consumer);

What is inside doesn't matter - the number of passed parameters into Iterable::forEach remains only one:

policies.forEach(policy -> {
    log.info("adding to database");
    test.addToDatabase(policy, stats);
    log.info("added to database");
});

There is theoretically an unlimited number of statements and variables you can work with. The only condition that whatever you use inside the lambda expression must be effectively final.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=388822&siteId=1