Czy wyrażenie lambda jest czymś więcej niż anonimową klasą wewnętrzną za pomocą jednej metody?

40

Pojawił się nowy szum z długo oczekiwanymi wyrażeniami lambda w Javie 8; co 3 dni pojawia się z nimi kolejny artykuł o tym, jak fajni są.

O ile rozumiem wyrażenie lambda jest niczym więcej niż anonimową klasą wewnętrzną z jedną metodą (przynajmniej na poziomie kodu bajtowego). Poza tym ma jeszcze jedną fajną funkcję wnioskowania o typie, ale uważam, że równoważność tego można uzyskać na pewnym poziomie za pomocą generycznych (oczywiście nie w tak zgrabny sposób, jak w przypadku wyrażeń lambda).

Wiedząc o tym, czy wyrażenia lambda przyniosą coś więcej niż tylko słodzenie syntaktyczne w Javie? Czy mogę tworzyć mocniejsze i bardziej elastyczne klasy lub inne konstrukcje obiektowe z wyrażeniami lambda, których nie można zbudować przy użyciu bieżących funkcji językowych?

m3th0dman
źródło
30
Gdy język jest kompletny, każdą dodaną funkcję można teoretycznie opisać jako „cukier składniowy”.
Philipp
34
@Philipp: To źle. Cechą charakterystyczną cukru syntaktycznego jest to, że usuwanie jest czysto lokalne i nie zmienia globalnej struktury programu. Istnieje wiele funkcji, których nie można wdrożyć za pomocą tylko lokalnych przekształceń. Na przykład, jeśli weźmiesz hipotetyczny język, który jest identyczny z Javą, ale z odpowiednimi wywołaniami tail, nie będzie możliwe zaprojektowanie wywołań tail do „czystej” Java bez globalnej transformacji całego programu.
Jörg W Mittag
2
@Philipp: Niestety, jest tylko jedna opinia za komentarzami, w przeciwnym razie głosowałbym za komentarzem Joerga 1000 razy. Nie, błędem jest to, że każdą cechę można opisać jako cukier składniowy. Cukier syntaktyczny nie dodaje nowej semantyki. W rzeczywistości AFAIK proponowane lambda Java NIE są składniowym cukrem dla specjalnych anonimowych klas wewnętrznych, ponieważ mają inną semantykę.
Giorgio
1
@Giorgio: i jaką mają semantykę?
user102008 17.04.13
2
@Giorgio: Tak, ale konwersja thisna OuterClass.thisjest częścią procesu odsławiania wyrażeń lambda na anonimową klasę.
user102008 17.04.13

Odpowiedzi:

47

tl; dr: chociaż jest to głównie cukier składniowy, ta ładniejsza składnia czyni wiele rzeczy praktycznymi, które kończyły się nieskończonymi, nieczytelnymi liniami nawiasów klamrowych i nawiasów.

Cóż, w rzeczywistości jest odwrotnie, ponieważ lambdas są znacznie starsze niż Java. Anonimowe klasy wewnętrzne za pomocą jednej metody są (były) najbliższą Javą dostępną dla lambdas. To przybliżenie, które było „wystarczająco dobre” przez jakiś czas, ale ma bardzo nieprzyjemną składnię.

Na powierzchni lambda Java 8 wydają się być niewiele więcej niż cukrem syntaktycznym, ale kiedy spojrzysz pod powierzchnię, zobaczysz mnóstwo interesujących abstrakcji. Na przykład specyfikacja JVM traktuje lambda zupełnie inaczej niż obiekt „prawdziwy” i chociaż można obsługiwać je tak, jakby były obiektami, JVM nie musi ich implementować.

Ale chociaż cała ta sztuczka techniczna jest interesująca i istotna (ponieważ umożliwia przyszłe optymalizacje w JVM!), Prawdziwą korzyścią jest „tylko” składniowa część cukrowa.

Co jest łatwiejsze do odczytania:

myCollection.map(new Mapper<String,String>() {
  public String map(String input) {
    return new StringBuilder(input).reverse().toString();
  }
});

lub:

myCollection.map(element -> new StringBuilder(element).reverse().toString());

lub (używając uchwytu metody zamiast lambda):

myCollection.map(String::toUpperCase);

Fakt, że w końcu możesz wyrazić w zwięzły sposób, który wcześniej byłby 5 liniami kodu (z których 3 są całkowicie nudne), wprowadza prawdziwą zmianę tego, co jest praktyczne (ale nie to, co jest możliwe, przyznane).

Joachim Sauer
źródło
1
Całkowicie zgadzam się z częścią cukrową składni; moje pytanie dotyczyło nowych konstrukcji obiektowych możliwych tylko z wyrażeniami lambda; w końcu Java jest językiem obiektowym. Pomyśl w porównaniu z lekami generycznymi i tym, jak kupili dużą elastyczność w Javie, elastyczność bez nich nieosiągalna.
m3th0dman
7
@ m3th0dman: właściwie generics nie dodał żadnych nowych umiejętności. A Listmoże pomieścić dowolny obiekt, List<String>może „tylko” przechowywać ciągi znaków. Generics dodał ograniczenie (i opcję sformalizowania ograniczeń!), A nie dodatek mocy. Prawie wszystko od czasu Java 1.1 jest cukrem syntaktycznym. I to niekoniecznie zła rzecz.
Joachim Sauer
3
@ m3th0dman: Właściwie generycy nie dodali żadnych nowych umiejętności, ponieważ w Javie są tylko cukrem składniowym. A List<String>jest tylko Listrzutowaniem, aby Stringdodać wokół niektórych zwracanych wartości. Niemniej jednak są one niezwykle przydatne i znacznie ułatwiają pisanie w języku. Podobnie jest w przypadku Lambdas.
Jan Hudec
3
Właściwie to znacznie więcej niż tylko cukier syntaktyczny. Wiele funkcji jest zaimplementowanych jako obywatel pierwszej klasy w JVM poprzez użycie wywołanego kodu bajtowego + kilka dość znacznych postępów w systemie wnioskowania typu. Nie jest zaimplementowany jako anonimowe klasy wewnętrzne.
Martijn Verburg
1
@JHHec: cóż, są one czymś więcej niż cukrem składniowym, ponieważ kompilator faktycznie je szanuje. Wykonawcze nie dba o nich, że to prawda.
Joachim Sauer
14

W przypadku Javy jest to lepszy sposób na stworzenie anonimowej klasy wewnętrznej. Wynika to z fundamentalnej decyzji w Javie, że każdy kawałek kodu bajtowego musi mieszkać w określonej klasie, której nie można zmienić po dziesięcioleciach kodu starszego typu.

Jednak nie o to tak naprawdę chodzi w wyrażeniach lambda. W formalizmach, w których są one rodzimymi pojęciami, a nie wyskokiem z wózka, lambdas są podstawowymi elementami składowymi; zarówno ich składnia, jak i stosunek ludzi do nich są bardzo różne. Przykład anonimowej funkcji rekurencyjnej utworzonej wyłącznie z lambd w Strukturze i interpretacji programów komputerowych może zmienić całą twoją koncepcję obliczeń. (Jestem jednak pewien, że ten sposób uczenia się programowania jest po prostu ezoteryczny, aby stać się historią sukcesu głównego nurtu.)

Kilian Foth
źródło
1
Uczenie się, że wszystko można zrealizować w kategoriach lambdów, jest bardzo pouczające, a nie moim zdaniem „ezoteryczne”. (Myślę jednak, że większość „programistów” nie przejmuje się interesującą teorią CS). To nie znaczy, że lambdas są wyjątkowe; oznacza to po prostu, że lambdy są jednym rodzajem uniwersalnej konstrukcji. Istnieją inne, takie jak kombinatory SKI. Lambda to podstawowe elementy składowe funkcjonalnych paradygmatów, ale może coś innego może być fundamentalne w innym paradygmacie.
user102008 16.04.13
+1 za „zniechęcenie do zawodów”: Często zastanawiam się, dlaczego niektóre języki ciągle się zmieniają, by naśladować inne popularne języki i dlaczego programiści to znoszą. Lubię lambdas i używam ich bardzo często w Scali, Lisp, Haskell, ale w Javie lub C ++ czują się jak refleksja nad tym językiem tylko dlatego, że stały się popularne w innych językach.
Giorgio
2

Tak, to tylko cukier syntaktyczny, w tym sensie, że gdziekolwiek napiszesz lambdę, możesz ponownie napisać to wyrażenie jako anonimowe wyrażenie klasy wewnętrznej za pomocą jednej metody, w której klasa implementuje interfejs funkcjonalny wywnioskowany z kontekstu lambda, i byłby dokładnie równoważny semantycznie. Możesz to zrobić, po prostu zastępując wyrażenie lambda anonimowym wyrażeniem klasy, bez zmiany jakiegokolwiek innego wyrażenia lub wiersza kodu.

użytkownik102008
źródło
1
dlaczego głosować? to, co powiedziałem, jest całkowicie poprawne
102008
To, co piszesz, jest rozsądną propozycją dla lambd Java, ale ta propozycja została odrzucona na rzecz innej ( doanduyhai.wordpress.com/2012/07/12/… ).
Giorgio
1
@Giorgio: Nic nie „proponuję”. Co dokładnie nie zgadzasz się z tym, co powiedziałem? Tak, wnętrze wyrażenia będzie wyglądać inaczej, np. Dodamy metodę i tak, thistrzeba będzie przekonwertować ją OuterClass.this. Nie przeczy to temu, co powiedziałem - każde wyrażenie lambda można przekształcić w semantycznie równoważne anonimowe wyrażenie klasowe, nie zmieniając niczego poza tym wyrażeniem . Nie powiedziałem, że wnętrze się nie zmieni.
user102008 17.04.13
3
To, co powiedziałeś, jest nieprawidłowe. Semantyka klas wewnętrznych lambda i anon nie jest dokładnie taka sama (choć są one podobne). Z czubka mojej głowy zmienia się znaczenie tych zmian; a anonowe klasy wewnętrzne zawsze skutkują nową instancją obiektu, podczas gdy lambda może, ale nie musi.
Stuart Marks
1
@StuartMarks: Nie, nie przeczytałeś mojej odpowiedzi. Nie powiedziałem, że każdy z nich może zrobić wszystko, co robi drugi. Powiedziałem, że lambda ma równoważną anonimową klasę, która jest semantycznie taka sama. Jak już powiedziałem w komentarzach, thisw lambda jest częścią cukru syntaktycznego i nie ma różnicy w semantyce. I o różnicach w implementacji, implementacji! = Semantyka. O różnicach w tożsamości obiektu; tożsamość obiektu jest niezwykle styczna do funkcjonalności lambd; i tak nikt nie sprawdza tożsamości obiektów klas lambdas / anon, ponieważ nie jest to przydatne.
user102008 17.04.13
1

Joachim Sauer wykonał już dobrą robotę, odpowiadając na twoje pytanie, ale wskazał tylko na coś, co uważam za ważne. Ponieważ Lambdas nie są klasami, nie są również kompilowane jako takie. Wszystkie anonimowe klasy wewnętrzne powodują utworzenie pliku .class, który z kolei musi zostać załadowany przez ClassLoader. Dlatego użycie Lambdas nie tylko sprawia, że ​​kod jest piękniejszy, ale także zmniejsza rozmiar skompilowanego kodu, wielkość pamięci ClassLoadera i czas potrzebny na przesłanie bitów z dysku twardego.

Christoph Grimmer-Dietrich
źródło
-1

Nie oni nie są.

Innym sposobem, by to ująć, jest to, że lambdas są zorientowanym na obiekt, ale zwięzłym, sposobem osiągnięcia tego samego, co klasa anonimowa lub, moim zdaniem, klasa wewnętrzna osiągnęłyby w sposób zorientowany obiektowo.

Guido Anselmi
źródło
1
Programowanie funkcjonalne może być całkowicie ortogonalne w stosunku do programowania obiektowego (patrz: Scala i F #). To brzmi jak opinia kogoś, kto zasadniczo nie lubi programowania funkcjonalnego.
KChaloux
1
@KChaloux - Nie funkcjonalny hejter programowania. Odpowiedź jest napisana przez kogoś, kto woli mieć młot ORAZ śrubokręt niż młotek. Programowanie OO jest dobrym paradygmatem, podobnie jak programowanie funkcjonalne. W języku preferuję język, który jasno określa jego intencje. Lambdas Czuję się błotnisty w związku z tym, że w innym przypadku Java ma charakter OO. Java nie została zaprojektowana jako język funkcjonalny. Ludzie potrzebują struktury, aby wykonywać swoją najlepszą pracę.
Guido Anselmi