Пропускают ли ссылки на методы накладные расходы на лямбда-оболочку? Могут ли они в будущем?
Согласно Учебному руководству по Java о методах :
Иногда ... лямбда-выражение делает только вызов существующего метода. В этих случаях часто проще обратиться к существующему методу по имени. Ссылки на методы позволяют вам сделать это; это компактные, легко читаемые лямбда-выражения для методов, которые уже имеют имя.
Я предпочитаю лямбда-синтаксис синтаксису ссылки на метод по нескольким причинам:
Лямбды понятнее
Несмотря на заявления Oracle, я нахожу лямбда-синтаксис более легким для чтения, чем сокращенная ссылка на метод объекта, поскольку синтаксис ссылки на метод неоднозначен:
Bar::foo
Вы вызываете статический метод с одним аргументом для класса x и передаете его x?
x -> Bar.foo(x)
Или вы вызываете метод экземпляра с нулевым аргументом для x?
x -> x.foo()
Синтаксис ссылки на метод может заменять любой из них. Он скрывает то, что на самом деле делает ваш код.
Лямбды безопаснее
Если вы ссылаетесь на Bar :: foo как на метод класса, а Bar позже добавляет метод экземпляра с тем же именем (или наоборот), ваш код больше не будет компилироваться.
Вы можете использовать лямбды последовательно
Вы можете обернуть любую функцию в лямбду - так что вы можете использовать один и тот же синтаксис везде. Синтаксис ссылки на метод не будет работать с методами, которые принимают или возвращают примитивные массивы, генерируют проверенные исключения или имеют то же имя метода, которое используется в качестве экземпляра, и статический метод (поскольку синтаксис ссылки на метод неоднозначен в отношении того, какой метод будет вызван) , Они не работают, когда вы перегружаете методы с одинаковым количеством аргументов, но вы все равно не должны этого делать (см. Пункт 41 Джоша Блоха), поэтому мы не можем отнести это к ссылкам на методы.
Заключение
Если за это не снижается производительность, у меня возникает соблазн отключить предупреждение в моей среде IDE и последовательно использовать лямбда-синтаксис, не добавляя случайную ссылку на метод в мой код.
PS
Ни здесь, ни там, но в моих снах ссылки на методы объекта выглядят больше так и применяют invoke-dynamic к методу непосредственно к объекту без лямбда-обертки:
_.foo()
.map(_.acceptValue())
Если я не ошибаюсь, это очень похоже на синтаксис Scala. Может быть, вы просто пытаетесь использовать не тот язык.System.out::println
чтобыforEach()
все время ...?Ответы:
Я думаю, что во многих случаях лямбда и метод-ссылка эквивалентны. Но лямбда обернет цель вызова объявленным типом интерфейса.
Например
Вы увидите консольный вывод трассировки стека.
В
lambda()
метод вызоваtarget()
являетсяlambda$lambda$0(InvokeTest.java:20)
, который имеет прослеживаемую данные строки. Очевидно, что это лямбда, которую вы пишете, компилятор генерирует анонимный метод для вас. И затем, вызывающий лямбда-метода является чем-то вродеInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
, то естьinvokedynamic
вызов в JVM, это означает, что вызов связан с сгенерированным методом .В
methodReference()
, вызов методаtarget()
является непосредственноInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, это означает, что вызов напрямую связан сInvokeTest::target
методом .Заключение
Прежде всего, сравните со ссылкой на метод, использование лямбда-выражения вызовет только еще один вызов метода для генерирующего метода из лямбды.
источник
Это все о метафории
Во-первых, большинство ссылок на методы не нуждается в десагеринге с помощью лямбда-метафории, они просто используются как ссылочный метод. В разделе «Лямбда-тело подслащивание» статьи перевода лямбда-выражений («TLE»):
Это далее подчеркивается далее в TLE "Лямбда-метафабрика":
Статические (
Integer::sum
) или неограниченные экземпляры метода instance (Integer::intValue
) являются «простейшими» или наиболее «удобными», в том смысле, что они могут оптимально обрабатываться метафорическим вариантом «быстрого пути» без десагеринга . Это преимущество полезно указать в метафакторных вариантах TLE:Естественно, ссылка на метод захвата экземпляра (
obj::myMethod
) должна предоставлять ограниченный экземпляр в качестве аргумента для дескриптора метода для вызова, что может означать необходимость десагеринга с использованием методов «моста».Заключение
Я не совсем уверен, о какой лямбда-оболочке вы намекаете, но, хотя конечный результат использования пользовательских лямбда-ссылок или ссылок на методы одинаков, способ, которым достигается, кажется, совсем другим, и может быть другим в будущем, если это не так сейчас. Следовательно, я полагаю, что более вероятно, чем нет, что метаданные могут обрабатывать ссылки на методы более оптимальным образом.
источник
При использовании лямбда-выражений есть одно довольно серьезное последствие, которое может повлиять на производительность.
Когда вы объявляете лямбда-выражение, вы создаете замыкание поверх локальной области видимости.
Что это значит и как это влияет на производительность?
Ну, я рад, что ты спросил. Это означает, что каждое из этих маленьких лямбда-выражений является небольшим анонимным внутренним классом, и это означает, что оно содержит ссылку на все переменные, которые находятся в той же области видимости лямбда-выражения.
Это также означает
this
ссылку на экземпляр объекта и все его поля. В зависимости от времени фактического вызова лямбды, это может привести к довольно значительной утечке ресурсов, поскольку сборщик мусора не может отпустить эти ссылки, пока объект, удерживающий лямбду, еще жив ...источник
this
объекту, на который можно сослаться. Следовательно, лямбда не пропускает ресурсы, просто имея для них область видимости; он держится только за те объекты, которые ему нужны. С другой стороны, анонимный внутренний класс может пропускать ресурсы, но не всегда делает это. См. Этот код для примера: a.blmq.us/2mmrL6v