Czy odniesienia do metod pomijają obciążenie owijarki lambda? Czy mogą w przyszłości?
Zgodnie z samouczkiem Java na temat metod :
Czasami ... wyrażenie lambda robi tylko wywołanie istniejącej metody. W takich przypadkach często łatwiej jest odnieść się do istniejącej metody według nazwy. Odwołania do metod umożliwiają to; są to zwarte, łatwe do odczytania wyrażenia lambda dla metod, które już mają nazwę.
Wolę składnię lambda od składni odwołania do metody z kilku powodów:
Baranki są wyraźniejsze
Pomimo twierdzeń Oracle, uważam, że składnia lambda jest łatwiejsza do odczytania niż skrót metody obiektowej, ponieważ składnia odwołania do metody jest niejednoznaczna:
Bar::foo
Czy wywołujesz statyczną metodę z jednym argumentem na klasie x i przekazujesz ją x?
x -> Bar.foo(x)
A może wywołujesz metodę wystąpienia z zerowym argumentem na x?
x -> x.foo()
Składnia odwołania do metody może oznaczać jedno lub drugie. Ukrywa to, co faktycznie robi Twój kod.
Jagnięta są bezpieczniejsze
Jeśli odniesiesz się do Bar :: foo jako metody klasy, a Bar później doda metodę instancji o tej samej nazwie (lub odwrotnie), twój kod nie będzie się już kompilował.
Możesz konsekwentnie używać lambdas
Możesz zawinąć dowolną funkcję w lambda - dzięki czemu możesz używać tej samej składni wszędzie. Składnia odwołania do metody nie działa na metodach, które pobierają lub zwracają prymitywne tablice, rzucają sprawdzone wyjątki lub używają tej samej nazwy metody jako instancji i metody statycznej (ponieważ składnia odwołania do metody jest niejednoznaczna co do tego, która metoda zostanie wywołana) . Nie działają, gdy przeciążono metody taką samą liczbą argumentów, ale i tak nie należy tego robić (patrz punkt 41 Josha Blocha), więc nie możemy tego powstrzymać przed odwołaniami do metod.
Wniosek
Jeśli nie ma za to obniżki wydajności, kusi mnie, aby wyłączyć ostrzeżenie w moim IDE i konsekwentnie korzystać ze składni lambda bez rozsypywania okazjonalnych odwołań do metod w moim kodzie.
PS
Ani tu, ani tam, ale w moich snach odwołania do metod obiektowych wyglądają bardziej tak i stosują metodę invoke-dynamic przeciwko metodzie bezpośrednio na obiekcie bez opakowania lambda:
_.foo()
.map(_.acceptValue())
: Jeśli się nie mylę, wygląda to bardzo podobnie do składni Scali. Może po prostu próbujesz użyć niewłaściwego języka.System.out::println
doforEach()
cały czas ...?Odpowiedzi:
W wielu scenariuszach myślę, że lambda i odwołanie do metody są równoważne. Ale lambda zawinie cel wywołania według deklarującego typu interfejsu.
Na przykład
Zobaczysz, jak konsola generuje plik stacktrace.
W
lambda()
metodzie wywoływanejtarget()
jest metodalambda$lambda$0(InvokeTest.java:20)
, która ma identyfikowalne informacje o linii. Oczywiście, to jest lambda, którą piszesz, kompilator generuje dla ciebie anonimową metodę. A następnie obiekt wywołujący metodę lambda jest czymś podobnymInvokeTest$$Lambda$2/1617791695.run(Unknown Source)
, to jestinvokedynamic
wywołanie w JVM, oznacza to, że wywołanie jest powiązane z wygenerowaną metodą .W
methodReference()
, wywołanie metodytarget()
jest bezpośrednioInvokeTest$$Lambda$1/758529971.run(Unknown Source)
, oznacza to, że wywołanie jest bezpośrednio powiązane zInvokeTest::target
metodą .Wniosek
Przede wszystkim, w porównaniu z referencją do metody, użycie wyrażenia lambda spowoduje tylko jedno wywołanie metody do metody generowania z lambda.
źródło
Chodzi o metafactory
Po pierwsze, większość odniesień do metod nie wymaga rozpracowywania przez metafabrykację lambda, są one po prostu stosowane jako metoda referencyjna. W sekcji „Lambda body sugaring” artykułu o tłumaczeniu wyrażeń lambda („TLE”):
Jest to dodatkowo podkreślone w dalszej części „The Lambda Metafactory”:
Odwołania do
Integer::sum
metody static ( ) lub niezwiązanej z instancją (Integer::intValue
) są „najprostsze” lub najbardziej „wygodne”, w tym sensie, że można je optymalnie obsłużyć za pomocą wariantu „szybkiej ścieżki” bez rozłączania . Ta przewaga jest pomocna w „Warunkach metafabryki” TLE:Oczywiście referencja do metody przechwytywania instancji (
obj::myMethod
) musi zawierać ograniczoną instancję jako argument do uchwytu metody w celu wywołania, co może oznaczać potrzebę odłączenia przy użyciu metod „pomostowych”.Wniosek
Nie jestem do końca pewien, na czym polega owijanie lambda, o czym sugerujesz, ale nawet jeśli ostateczny wynik użycia zdefiniowanych przez ciebie lambd lub referencji metod jest taki sam, sposób , w jaki został osiągnięty, wydaje się zupełnie inny, i może być inaczej w przyszłości, jeśli teraz tak nie jest. Dlatego przypuszczam, że bardziej prawdopodobne jest, że odniesienia do metod mogą być obsługiwane przez metafactory w bardziej optymalny sposób.
źródło
Istnieje jedna dość poważna konsekwencja przy użyciu wyrażeń lambda, które mogą wpływać na wydajność.
Kiedy deklarujesz wyrażenie lambda, tworzysz zamknięcie w zakresie lokalnym.
Co to oznacza i jak wpływa na wydajność?
Cieszę się, że zapytałeś. Oznacza to, że każde z tych małych wyrażeń lambda jest małą anonimową klasą wewnętrzną, co oznacza, że zawiera odniesienie do wszystkich zmiennych, które są w tym samym zakresie wyrażenia lambda.
Oznacza to także
this
odwołanie do instancji obiektu i wszystkich jego pól. W zależności od czasu rzeczywistego wywołania lambda może to oznaczać dość znaczny wyciek zasobów, ponieważ śmieciarz nie jest w stanie puścić tych odniesień, dopóki obiekt trzymający lambda wciąż żyje ...źródło
this
obiektu, do którego można się odwoływać. Dlatego lambda nie przecieka zasobów, po prostu przez ich pole manewru; trzyma tylko przedmioty, których potrzebuje. Z drugiej strony anonimowa klasa wewnętrzna może przeciekać zasoby, ale nie zawsze to robi. Zobacz ten kod jako przykład: a.blmq.us/2mmrL6v