Где оцениваются лямбда-выражения Java 8?

Являются ли лямбда-выражения оцененными в том месте, где мы пишем их или в любом другом классе Java?

Например:

Stream<Student> absent =  students.values().stream().filter(s -> !s.present());

Будет ли приведенное выше выражение лямбда передано методу фильтра немедленно исполнено в данном классе, где код написан OR в другом классе, и потребуется больше времени (с точки зрения nano секунд), чем если бы код был написан в обычный стиль кодирования до Java 8?

Ответы

Ответ 1

Когда вы компилируете свои источники, компилятор вставляет инструкцию invokedynamic byte code для используемого выражения лямбда. Фактическая реализация (которая в вашем случае является Predicate) будет создана во время выполнения через ASM. Он даже не будет присутствовать на жестком диске при его запуске - это означает, что класс генерируется в памяти, для Predicate не будет файла .class. Это большая разница между анонимным классом, например, который сгенерирует файл class при его компиляции.

Вы можете увидеть сгенерированный файл для Predicate, если вы запустите свой пример с помощью

-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here

В противном случае ответ Eran правильный, Stream управляется операцией терминала, если таковой нет, ничего не выполняется. Вы должны абсолютно прочитать отличный ответ Холгера о еще более интересных отличиях.

Ответ 2

Тело выражения лямбда, переданного методу фильтра в вашем примере, вообще не будет выполняться, поскольку filter является промежуточной операцией, которая выполняется только для Stream, которая заканчивается в терминальной операции, такая как collect, forEach и т.д.

Если вы добавите операцию терминала, например, собирая элементы Stream до a List:

List<Student> absent =  students.values().stream().filter(s -> !s.present()).collect(Collectors.toList());

тело выражения лямбда будет выполнено для каждого элемента вашего Stream, чтобы операция терминала могла выдавать свой вывод.

Обратите внимание, что это поведение не изменится, если вы передали экземпляр анонимного класса или какую-либо другую реализацию интерфейса Predicate для вашего метода filter вместо выражения лямбда.

Ответ 3

Выражения ленивы оцениваются, что означает, что они будут фактически оцениваться только при попытке "прекратить" поток, т.е. использовать операцию, которая принимает поток, но возвращает что-то еще, например collect, min, max, reduce и т.д. Операции, которые принимают поток в качестве входных данных и возвращают поток в качестве вывода, обычно ленивы.

Ответ 4

Лямбда-выражения - это, по сути, объекты с единственным методом, поэтому они оцениваются всякий раз, когда вызывается этот метод.

В вашем конкретном случае они никогда не оцениваются. Поток не оценивает выражения до тех пор, пока вы не назовете операцию завершения (collect, findAny и т.д.)