Почему существует 2 кадра стека для вызова лямбды?

Следующий код:

public static void main(String[] args) {
    Collections.singleton(1).stream().forEach(i -> new Exception().printStackTrace());
}

печатает:

java.lang.Exception
    at PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)
    at PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)
    at java.util.Collections$2.tryAdvance(Collections.java:4717)
    at java.util.Collections$2.forEachRemaining(Collections.java:4725)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at PrintLambdaStackTrace.main(PrintLambdaStackTrace.java:6)

Как реализуется лямбда-вызов? Почему существует 2 кадра стека?

Ответ 1

PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)

Это сгенерированный класс, который реализует требуемый интерфейс. Его метод accept является просто заглушкой, делегируя метод, который был сгенерирован во время компиляции и добавлен в класс PrintLambdaStackTrace. Этот класс генерируется при времени привязки лямбда (первый раз, когда нужно создать экземпляр лямбды).

PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)

Это метод, который фактически реализует лямбда-поведение. Он принадлежит классу PrintLambdaStackTrace.