Где оцениваются лямбда-выражения 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
и т.д.)