当 Consumer 只接受一个参数时,为什么 forEach 方法接受调用具有多个参数的方法的 lambda?

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

我正在 List<String> 上玩 forEach,但我对为什么可以接受以下行感到困惑:

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

由于forEach需要一个Consumer,而Consumeraccept方法只有一个参数,我不明白为什么调用addtoDatabase是可以接受的,因为它需要两个参数。请参阅下面的完整测试代码。请注意,我只是在这里玩来学去,所以这段代码并不意味着完美或优雅。

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

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));

是的。您将 Iterable::forEach 的参数与 lambda 表达式中语句的参数混淆了。由于 Iterable::forEach is Consumer<T> 中唯一的参数只不过是完全相同的匿名 class:

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

policies.forEach(consumer);

等同于:

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

里面的内容无关紧要 - 传入 Iterable::forEach 的参数数量仍然只有一个:

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

理论上,您可以使用无限数量的语句和变量。在 lambda 表达式中使用的唯一条件必须是 effectively final.