Чи є лямбда-вираз чимось більшим, ніж анонімний внутрішній клас одним методом?


40

У Java 8 з'явився новий галас із довгоочікуваними лямбдаськими виразами; кожні 3 дні з ними з’являється ще одна стаття про те, як вони круті.

Наскільки я зрозумів лямбда-вираз - це не що інше, як анонімний внутрішній клас одним методом (принаймні, на рівні байт-коду). Окрім цього, це має ще одну приємну особливість - вивід типу, але я вважаю, що еквівалент цього може бути досягнутий на загальному рівні (звичайно, не таким акуратним чином, як з лямбда-виразами).

Знаючи це, чи лямбда-вирази принесуть щось більше, ніж просто синтаксичне цукор на Java? Чи можу я створити більш потужні та гнучкі класи або інші об'єктно-орієнтовані конструкції з лямбда-виразами, які неможливо побудувати за допомогою сучасних мовних особливостей?


30
Коли мова є цілковитою, кожна додана функція теоретично може бути описана як "синтаксичний цукор".
Філіпп

34
@Philipp: Це неправильно. Визначальною характеристикою синтаксичного цукру є те, що знежирення є суто локальним і не змінює глобальної структури програми. Є безліч функцій, які неможливо реалізувати лише за допомогою локальних перетворень. Наприклад, якщо ви користуєтесь гіпотетичною мовою, ідентичною Java, але при правильних викликах хвоста, не вдасться знешкодити хвостові дзвінки на "чисту" Java без глобальної трансформації всієї програми.
Йорг W Міттаг

2
@Philipp: На жаль, є лише одна пропозиція щодо коментарів, інакше я б схвалив коментар Йорга 1000 разів. Ні, це неправильно, що будь-яку особливість можна охарактеризувати як синтаксичний цукор. Синтаксичний цукор не додає нової семантики. Насправді, AFAIK, запропоновані лямбдами Java, НЕ є синтаксичним цукром для спеціальних анонімних внутрішніх класів, оскільки вони мають різну семантику.
Джорджіо

1
@Giorgio: і яку різну семантику вони мають?
користувач102008

2
@Giorgio: Так, але перетворення на thisв OuterClass.thisє частиною процесу знецінення лямбда-виразів в анонімний клас.
користувач102008

Відповіді:


47

tl; dr: хоча це в основному синтаксичний цукор, цей приємний синтаксис робить багато речей практичним, які раніше закінчувалися нескінченними, нечитабельними рядками дужок і дужок.

Ну, насправді навпаки, так як лямбди набагато старші за Яву. Анонімні внутрішні класи з єдиним методом є (були) найближчими до Java прийшли лямбдами. Це наближення, яке було "досить добре" деякий час, але має дуже неприємний синтаксис.

На поверхні, лямбди Java 8, здається, не набагато більше, ніж синтаксичний цукор, але, дивлячись під поверхню, ви бачите безліч цікавих абстракцій. Наприклад, специфікація JVM трактує лямбда зовсім інакше, ніж "справжній" об'єкт, і хоча ви можете поводитися з ними так, як ніби вони там, де об'єкти, JVM не зобов'язаний реалізувати їх як такі.

Але хоча вся ця технічна хитрість цікава та актуальна (оскільки вона дозволяє майбутні оптимізації в JVM!), Справжня користь - це "просто" синтаксична частина цукру.

Що легше читати:

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

або:

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

або (використовуючи ручку методу замість лямбда):

myCollection.map(String::toUpperCase);

Той факт, що можна нарешті висловити стисло, що раніше було б 5 рядків коду (з яких 3 абсолютно нудні), приносить реальну зміну практичного (але не того, що можливо, наданого).


1
Я повністю погоджуюся з синтаксичною частиною цукровари; моє запитання стосувалося нових об'єктно-орієнтованих конструкцій, можливих лише з лямбда-виразами; адже Java - об'єктно-орієнтована мова. Подумайте в порівнянні з дженериками, і як вони придбали багато гнучкості на Java, гнучкість без них неможлива.
m3th0dman

7
@ m3th0dman: насправді генерика не додала нових здібностей. A Listміг би вмістити будь-який об'єкт, а List<String>"лише" може містити рядки. Generics додав обмеження (і можливість формалізувати обмеження!), А не додаток у владі. Майже все, оскільки Java 1.1 була синтаксичним цукром. І це не обов’язково погано.
Йоахім Зауер

3
@ m3th0dman: Насправді генерики не додали нових здібностей, тому що в Java вони є лише синтаксичним цукром. A List<String>- це лише Listлитий Stringдодаток, який додається навколо деяких повернених значень. Проте вони надзвичайно корисні та зробили мову набагато зручнішою для написання. Лямбди - подібний випадок.
Ян Худек

3
Насправді це набагато більше, ніж просто синтаксичний цукор. Значна частина функціональних можливостей реалізована як громадянин першого класу в JVM за допомогою використання викликаного динамічного байт-коду + деякі досить здоровенні досягнення системи виводу типу. Це не реалізовано як анонімні внутрішні класи.
Мартійн Вербург

1
@JanHudec: ну вони трохи більше, ніж синтаксичний цукор, бо компілятор насправді їх поважає. Час виконання їх не хвилює, це правда.
Йоахім Зауер

14

Для Java, так, це не що інше, як кращий спосіб створення анонімного внутрішнього класу. Це пов’язано з принциповим рішенням Java, що кожен біт-код байта повинен жити в межах конкретного класу, який зараз неможливо змінити після десятиліть застарілого коду, який слід розглядати.

Однак це не те, що насправді йдеться про лямбдаські вирази. У формалізмах, де вони є ріднішими поняттями, а не стрибками на контуру, лямбда є основними складовими; і їх синтаксис, і ставлення людей до них дуже різні. Приклад анонімної рекурсивної функції, створеної виключно з лямбда в структурі та інтерпретації комп'ютерних програм , здатний змінити всю вашу концепцію обчислення. (Однак я майже впевнений, що спосіб навчання програмуванню - це просто езотерика, що коли-небудь стане основною історією успіху.)


1
Дізнатися, що все можна реалізувати з точки зору лямбдасів, є дуже повчальним і не є "езотеричним" на мою думку. (Однак, мабуть, більшість "кодерів" не цікавиться цікавою теорією CS.) Це не означає, що лямбдас особливий; це просто означає, що лямбда - це один тип універсальної конструкції. Є й інші, як комбінатори SKI. Лямбди - це фундаментальні складові у функціональних парадигмах, але, можливо, щось інше може бути фундаментальним у іншій парадигмі.
користувач102008

+1 для "контурної стрічки": Мені часто цікаво, чому деякі мови продовжують змінюватися, щоб імітувати інші популярні мови і чому програмісти миряться з нею. Мені подобаються лямбдаси і багато їх використовую в Scala, Lisp, Haskell, але в Java або C ++ вони відчувають себе думкою, що зациклюється на мові лише тому, що вони стали популярними в інших мовах.
Джорджіо

2

Так, це просто синтаксичний цукор, в тому сенсі, що де б ви не писали лямбда, ви можете переписати це вираз як анонімний вираз внутрішнього класу одним методом, коли клас реалізує функціональний інтерфейс, який можна зробити для контексту лямбда, і це було б абсолютно рівнозначно семантично. А це можна зробити, просто замінивши вираз лямбда на вираз анонімного класу, не змінюючи жодного іншого виразу чи рядка коду.


1
навіщо знижувати? те, що я сказав цілком правильно
user102008

Те, що ви пишете, здається розумною пропозицією для Java-лямбда, але ця пропозиція була відхилена на користь іншого ( doanduyhai.wordpress.com/2012/07/12/… ).
Джорджіо

1
@Giorgio: Я нічого не "пропоную". Що саме ви не згодні з тим, що я сказав? Так, всередині виразу буде виглядати інакше, наприклад, ми додамо метод, і так, thisпотрібно буде перетворити OuterClass.this. Це не суперечить тому, що я сказав - кожен лямбда- вираз може бути перетворений у семантично-еквівалентний вираз анонімного класу, не змінюючи нічого поза цим виразом . Я не сказав, що всередині не зміниться.
користувач102008

3
Те, що ви сказали, невірно. Семантика лямбда та анонів внутрішніх класів не зовсім однакова (хоча вони схожі). Зверху моєї голови зміст сенсу цього змінює; а серед внутрішніх класів завжди виникає новий екземпляр об'єкта, тоді як лямбда може чи не може.
Стюарт Маркс

1
@StuartMarks: Ні, ви не прочитали моєї відповіді. Я не сказав, що кожен з них може робити все, що робить інші. Я сказав, що лямбда має еквівалентний анонімний клас, який семантично однаковий. Як я вже говорив у коментарях, thisлямбда - це частина синтаксичного цукру, і це не є різницею в семантиці. А про відмінності в реалізації, реалізації! = Семантика. Про відмінності в ідентичності об'єкта; ідентичність об'єкта надзвичайно дотична до функціональності лямбдів; ніхто не перевіряє ідентичність об'єктів лямбда / анонів класів, так як це не корисно.
користувач102008

1

Йоахім Зауер уже добре зробив відповідь на ваше запитання, але лише натякнув на те, що я вважаю важливим. Оскільки Лямбдас не є класами, вони також не складаються як такі. Усі анонімні внутрішні класи призводять до створення файлу .class, який, у свою чергу, повинен завантажуватися ClassLoader. Тому використання Lambdas не тільки робить ваш код красивішим, але й зменшує розмір скомпільованого коду, слід пам’яті вашого ClassLoader та час, необхідний для перенесення бітів з вашого жорсткого диска.


-1

Ні, вони не.

Інший спосіб сказати, що це лямбда - це не об'єктно-орієнтований, але стислий спосіб досягти того самого, що мали б анонімний клас або, на мою перевагу, внутрішній клас, об'єктно-орієнтованим способом.


1
Функціональне програмування може бути абсолютно ортогональним для об'єктно-орієнтованого програмування (див. Scala та F #). Це виглядає як думка того, хто в принципі просто не любить функціональне програмування.
KChaloux

1
@KChaloux - Не функціональний ненависник програмування. Відповідь пише хтось, хто вважає за краще молоток І викрутку, а не Молоток. Програмування ОО - це хороша парадигма, як і функціональне програмування. Моя перевага в мові полягає в тому, щоб вона була чіткою щодо її намірів. Лямбдас Я відчуваю себе каламутною інакше OO природи Яви. Java не була розроблена як функціональна мова. Людям потрібна структура, щоб зробити найкращу роботу.
Гвідо Ансельмі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.