Czy wyrażenia lambda mają inne zastosowanie niż zapisywanie wierszy kodu?
Czy są jakieś specjalne funkcje zapewniane przez lambdy, które rozwiązały problemy, które nie były łatwe do rozwiązania? Typowe użycie, które widziałem, jest takie, że zamiast pisać to:
Comparator<Developer> byName = new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
};
Możemy użyć wyrażenia lambda, aby skrócić kod:
Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());
(o1, o2) -> o1.getName().compareTo(o2.getName())
Comparator.comparing(Developer::getName)
Odpowiedzi:
Wyrażenia lambda nie zmieniają ogólnie zestawu problemów, które można rozwiązać za pomocą Javy, ale zdecydowanie ułatwiają rozwiązywanie pewnych problemów, z tego samego powodu, dla którego nie programujemy już w języku asemblerowym. Usunięcie zbędnych zadań z pracy programisty ułatwia życie i pozwala robić rzeczy, których inaczej byśmy nawet nie tknęli, tylko dla ilości kodu, który musiałbyś wyprodukować (ręcznie).
Ale wyrażenia lambda to nie tylko zapisywanie wierszy kodu. Wyrażenia lambda umożliwiają definiowanie funkcji , w przypadku których wcześniej można było użyć anonimowych klas wewnętrznych jako obejścia, dlatego w takich przypadkach można zastąpić anonimowe klasy wewnętrzne, ale nie ogólnie.
Przede wszystkim wyrażenia lambda są definiowane niezależnie od interfejsu funkcjonalnego, na który będą konwertowane, więc nie ma dziedziczonych elementów, do których mogliby uzyskać dostęp, a ponadto nie mogą uzyskać dostępu do wystąpienia typu implementującego interfejs funkcjonalny. W wyrażeniu lambda
this
isuper
mają takie samo znaczenie jak w otaczającym kontekście, zobacz także tę odpowiedź . Nie można także tworzyć nowych zmiennych lokalnych, które przesłaniają zmienne lokalne otaczającego kontekstu. W przypadku zamierzonego zadania definiowania funkcji usuwa to wiele źródeł błędów, ale oznacza to również, że w innych przypadkach użycia mogą istnieć anonimowe klasy wewnętrzne, których nie można przekonwertować na wyrażenie lambda, nawet jeśli implementuje interfejs funkcjonalny.Ponadto konstrukcja
new Type() { … }
gwarantuje utworzenie nowej odrębnej instancji (jaknew
zawsze). Anonimowe instancje klas wewnętrznych zawsze zachowują odniesienie do ich wystąpienia zewnętrznego, jeśli zostały utworzone w innymstatic
kontekście. W przeciwieństwie do tego wyrażenia lambda przechwytują odniesienie tylkothis
wtedy, gdy jest to potrzebne, tj. Jeśli mają dostępthis
lub nie sąstatic
członkami. Tworzą instancje celowo nieokreślonej tożsamości, co pozwala implementacji zdecydować w czasie wykonywania, czy ponownie wykorzystać istniejące instancje (zobacz także „ Czy wyrażenie lambda tworzy obiekt na stercie za każdym razem, gdy jest wykonywane? ”).Te różnice dotyczą twojego przykładu. Twoja anonimowa konstrukcja klasy wewnętrznej zawsze będzie tworzyła nową instancję, może również przechwytywać odwołanie do instancji zewnętrznej, podczas gdy twoje
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName())
jest nieprzechwytywanym wyrażeniem lambda, które w typowych implementacjach zostanie oszacowane na singleton. Co więcej, nie tworzy.class
pliku na dysku twardym.Biorąc pod uwagę różnice zarówno semantyczne, jak i wydajnościowe, wyrażenia lambda mogą zmienić sposób, w jaki programiści będą rozwiązywać pewne problemy w przyszłości, oczywiście również z powodu nowych interfejsów API obejmujących koncepcje programowania funkcjonalnego z wykorzystaniem nowych funkcji języka. Zobacz także wyrażenie lambda Java 8 i wartości pierwszej klasy .
źródło
Języki programowania nie są przeznaczone do wykonywania przez maszyny.
Są dla programistów do myślenia .
Języki to rozmowa z kompilatorem, mająca na celu przekształcenie naszych myśli w coś, co maszyna może wykonać. Jedną z głównych skarg na Javę ze strony osób, które przychodzą do niej z innych języków (lub zostawiają ją dla innych języków) było to, że wymusza ona pewien model mentalny na programiście (tj. Wszystko jest klasą).
Nie będę się zastanawiać, czy to dobrze, czy źle: wszystko to kompromisy. Ale lambdy Java 8 pozwalają programistom myśleć w kategoriach funkcji , czego wcześniej nie można było robić w Javie.
To to samo, co programista proceduralny, który uczy się myśleć w kategoriach klas, gdy przychodzą do języka Java: widzisz, jak stopniowo odchodzą od klas, które są gloryfikowanymi strukturami i mają klasy `` pomocnicze '' z zestawem metod statycznych i przechodzą do czegoś, co bardziej przypomina racjonalny projekt OO (mea culpa).
Jeśli pomyślisz o nich jako o krótszym sposobie wyrażania anonimowych klas wewnętrznych, to prawdopodobnie nie uznasz ich za bardzo imponujące w taki sam sposób, w jaki programista proceduralny powyżej prawdopodobnie nie sądził, że klasy były znacznym ulepszeniem.
źródło
Zapisywanie wierszy kodu może być postrzegane jako nowa funkcja, jeśli umożliwia napisanie znacznej części logiki w krótszy i wyraźniejszy sposób, co zajmuje innym osobom mniej czasu na przeczytanie i zrozumienie.
Bez wyrażeń lambda (i / lub odwołań do metod)
Stream
potoki byłyby znacznie mniej czytelne.Pomyśl na przykład, jak
Stream
wyglądałby poniższy potok, gdybyś zastąpił każde wyrażenie lambda anonimową instancją klasy.To byłby:
Jest to dużo trudniejsze do napisania niż wersja z wyrażeniami lambda i dużo bardziej podatne na błędy. Trudniej też to zrozumieć.
A to jest stosunkowo krótki rurociąg.
Aby uczynić to czytelnym bez wyrażeń lambda i odniesień do metod, należałoby zdefiniować zmienne, które przechowują różne używane tutaj wystąpienia interfejsu funkcjonalnego, co spowodowałoby podzielenie logiki potoku, utrudniając zrozumienie.
źródło
Comparator
for,sorted
ponieważ i tak porównuje się w naturalnej kolejności. Może zmienić to na porównanie długości sznurków lub podobnych, żeby przykład był jeszcze lepszy?compareToIgnoreCase
żeby zachowywał się inaczej niżsorted()
.compareToIgnoreCase
nadal jest złym przykładem, ponieważ możesz po prostu użyć istniejącegoString.CASE_INSENSITIVE_ORDER
komparatora. Sugestia @ tobias_k dotycząca porównywania według długości (lub jakiejkolwiek innej właściwości bez wbudowanego komparatora) pasuje lepiej. Sądząc po przykładach kodu SO Q&A, lambdy spowodowały wiele niepotrzebnych implementacji komparatorów…Iteracja wewnętrzna
Podczas iteracji kolekcji Java większość programistów zwykle pobiera element, a następnie go przetwarza . To znaczy, wyjmij ten element, a następnie użyj go lub włóż ponownie, itp. W przypadku wersji Java starszych niż 8 można zaimplementować klasę wewnętrzną i zrobić coś takiego:
Teraz z Javą 8 możesz robić lepiej i mniej rozwlekle z:
albo lepiej
Zachowania jako argumenty
Zgadnij następujący przypadek:
Dzięki interfejsowi Java 8 Predicate możesz zrobić tak lepiej:
Nazywając to tak:
Źródło: DZone - Why We Need Lambda Expressions in Java
źródło
numbers.forEach((Integer value) -> System.out.println(value));
wyrażenie lambda składa się z dwóch części: jednej po lewej stronie symbolu strzałki (->) zawierającej parametry, a po prawej zawierającej jego treść . Kompilator automatycznie ustala, że wyrażenie lambda ma taką samą sygnaturę, jak jedyna niezaimplementowana metoda interfejsu konsumenta.Istnieje wiele korzyści z używania lambd zamiast klas wewnętrznych, jak poniżej:
Uczyń kod bardziej zwartym i wyrazistym bez wprowadzania większej semantyki składni języka. podałeś już przykład w swoim pytaniu.
Korzystając z lambd, z przyjemnością programujesz z operacjami w stylu funkcjonalnym na strumieniach elementów, takich jak przekształcenia map-redukuj w kolekcjach. zobacz dokumentację pakietów java.util.function i java.util.stream .
Nie ma pliku klas fizycznych generowanych przez kompilator dla lambd. W ten sposób sprawia, że dostarczane aplikacje są mniejsze. Jak pamięć przypisuje lambda?
Kompilator zoptymalizuje tworzenie lambda, jeśli lambda nie uzyskuje dostępu do zmiennych poza swoim zakresem, co oznacza, że instancja lambda zostanie utworzona tylko raz przez JVM. Aby uzyskać więcej informacji, zobacz odpowiedź @ Holger na pytanie Czy buforowanie odwołań do metod to dobry pomysł w Javie 8? .
Lambdy mogą implementować interfejsy z wieloma markerami oprócz interfejsu funkcjonalnego, ale anonimowe klasy wewnętrzne nie mogą implementować więcej interfejsów, na przykład:
źródło
Lambdy to po prostu cukier syntaktyczny dla anonimowych zajęć.
Przed lambdami można użyć anonimowych klas, aby osiągnąć to samo. Każde wyrażenie lambda można przekonwertować na klasę anonimową.
Jeśli używasz IntelliJ IDEA, może wykonać konwersję za Ciebie:
źródło
Odpowiadając na twoje pytanie, faktem jest, że lambdy nie pozwalają ci zrobić niczego, czego nie mogłeś zrobić przed Java-8, a raczej pozwalają ci napisać bardziej zwięzły kod. Zaletą tego jest to, że Twój kod będzie bardziej przejrzysty i bardziej elastyczny.
źródło
Jedną rzeczą, o której jeszcze nie wspomniałem, jest to, że lambda pozwala zdefiniować funkcjonalność tam, gdzie jest używana .
Więc jeśli masz jakąś prostą funkcję selekcyjną, nie musisz umieszczać jej w osobnym miejscu z kilkoma szablonami, po prostu piszesz lambdę, która jest zwięzła i odpowiednia lokalnie.
źródło
Tak, jest wiele zalet.
źródło