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


40

В Java 8 появилась новая реклама с долгожданными лямбда-выражениями; каждые 3 дня появляется новая статья о том, как они круты.

Насколько я понял, лямбда-выражение - это не что иное, как анонимный внутренний класс с одним методом (по крайней мере, на уровне байт-кода). Помимо этого он имеет еще одну приятную особенность - вывод типа, но я считаю, что эквивалент этого можно достичь с помощью обобщений на некотором уровне (конечно, не таким аккуратным способом, как с лямбда-выражениями).

Зная это, принесут ли лямбда-выражения нечто большее, чем просто синтаксический подсахаринг в Java? Могу ли я создать более мощные и гибкие классы или другие объектно-ориентированные конструкции с лямбда-выражениями, которые невозможно построить с использованием текущих возможностей языка?


30
Когда язык завершен по Тьюрингу, каждая добавленная функция теоретически может быть описана как «синтаксический сахар».
Филипп

34
@ Филипп: Это неправильно. Определяющей характеристикой синтаксического сахара является то, что десугаринг является чисто локальным и не меняет глобальную структуру программы. Существует множество функций, которые невозможно реализовать с помощью локальных преобразований. Например, если вы возьмете гипотетический язык, идентичный Java, но с надлежащими хвостовыми вызовами, было бы невозможно десугаровать хвостовые вызовы в «чистую» Java без глобального преобразования всей программы.
Йорг Миттаг,

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

1
@ Джорджио: а какая у них семантика?
user102008

2
@ Джорджио: Да, но преобразование thisв OuterClass.this- это часть процесса удаления лямбда-выражений в анонимном классе.
user102008

Ответы:


47

tl; dr: хотя это в основном синтаксический сахар, этот более приятный синтаксис делает множество практических вещей, которые заканчивались бесконечными, нечитаемыми строками скобок и скобок.

Ну, на самом деле все наоборот, поскольку лямбды намного старше, чем Java. Внутренние анонимные классы с одним методом являются (были) наиболее близкими к лямбдам 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>может «только» содержать строки. Обобщения добавили ограничение (и возможность формализовать ограничения!), А не добавление к власти. Почти все, что с тех пор, как Java 1.1 стала синтаксическим сахаром. И это не обязательно плохо.
Иоахим Зауэр

3
@ m3th0dman: На самом деле дженерики не добавили новых возможностей, потому что в Java они просто синтаксический сахар. A List<String>- это просто Listприведение к Stringнекоторым возвращаемым значениям. Тем не менее, они чрезвычайно полезны и сделали язык намного более удобным для написания. Лямбды похожи.
Ян Худек

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

1
@JanHudec: ну, они немного больше, чем синтаксический сахар, потому что компилятор действительно уважает их. Среда не заботится о них, что это правда.
Иоахим Зауэр

14

Да, для Java это не что иное, как лучший способ создания анонимного внутреннего класса. Это из-за фундаментального решения в Java, что каждый бит байтового кода должен жить в определенном классе, который не может быть изменен сейчас после десятилетий унаследованного кода.

Однако это не то, о чем на самом деле говорят лямбда-выражения. В формализмах, где они являются нативными понятиями, а не искривлением "скачка на поводке", лямбды являются фундаментальными строительными блоками; и их синтаксис, и отношение людей к ним очень разные. Пример анонимной рекурсивной функции, созданной исключительно из лямбд в Структуре и интерпретации компьютерных программ , способен изменить всю вашу концепцию вычислений. (Тем не менее, я совершенно уверен, что способ научиться программировать - это просто эзотерика, чтобы стать историей успеха.)


1
Узнать, что все можно реализовать в терминах лямбд, очень поучительно и, на мой взгляд, не «эзотерично». (Тем не менее, я полагаю, что большинство «программистов» не интересуются интересной теорией CS). Это не значит, что лямбды особенные; это просто означает, что лямбды являются одним из типов универсальной конструкции. Есть и другие, как комбинаторы SKI. Лямбды являются фундаментальными строительными блоками в функциональных парадигмах, но, возможно, что-то еще может быть фундаментальным в другой парадигме.
user102008

+1 за «искривление в прыжке»: я часто задаюсь вопросом, почему некоторые языки постоянно меняются, подражая другим популярным языкам, и почему программисты терпят это. Я люблю лямбды и часто их использую в Scala, Lisp, Haskell, но в Java или C ++ они чувствуют себя запоздалой мыслью о языке только потому, что стали популярными в других языках.
Джорджио

2

Да, это просто синтаксический сахар, в том смысле, что где бы вы ни писали лямбду, вы можете переписать это выражение как анонимное выражение внутреннего класса с помощью одного метода, где класс реализует функциональный интерфейс, выведенный для контекста лямбды, и это было бы точно эквивалентно семантически. И вы можете сделать это, просто заменив лямбда-выражение анонимным выражением класса, не изменяя любое другое выражение или строку кода.


1
почему понизить? то, что я сказал, совершенно правильно
user102008

То, что вы пишете, звучит разумно для Java-лямбд, но это предложение было отклонено в пользу другого ( doanduyhai.wordpress.com/2012/07/12/… ).
Джорджио

1
@ Джорджио: Я ничего не "предлагаю". Что именно вы не согласны с тем, что я сказал? Да, внутренняя часть выражения будет выглядеть иначе, например, мы добавим метод, и да, thisего нужно будет преобразовать в OuterClass.this. Это не противоречит тому, что я сказал - каждое лямбда- выражение может быть преобразовано в семантически эквивалентное выражение анонимного класса, не изменяя ничего вне этого выражения . Я не говорил, что внутренняя часть не изменится.
user102008

3
То, что вы сказали, неверно. Семантика лямбда- и аноновых внутренних классов не совсем одинакова (хотя они схожи). С вершины моей головы, смысл значения этого изменяется; и только внутренние классы всегда приводят к новому экземпляру объекта, тогда как лямбда может или не может.
Стюарт Маркс

1
@StuartMarks: Нет, вы не читали мой ответ. Я не говорил, что каждый из них может делать все, что делает другой. Я сказал, что у лямбды есть эквивалентный анонимный класс, который семантически одинаков. Как я уже говорил в комментариях, thisлямбда является частью синтаксического сахара и не является разницей в семантике. И о различиях в реализации, реализации! = Семантика. О различиях в идентичности объекта; идентичность объекта чрезвычайно касательна к функциональности лямбд; в любом случае никто не проверяет объектную идентичность классов lambdas / anon, потому что это бесполезно.
user102008

1

Йоахим Зауэр уже хорошо поработал, отвечая на ваш вопрос, но только намекнул на то, что я считаю важным. Поскольку лямбды не являются классами, они также не компилируются как таковые. Все анонимные внутренние классы приводят к созданию файла .class, который, в свою очередь, должен быть загружен ClassLoader. Таким образом, использование Lambdas вместо этого не только делает ваш код более красивым, но также уменьшает размер вашего скомпилированного кода, объем памяти вашего ClassLoader и время, необходимое для передачи битов с вашего жесткого диска.


-1

Нет, они не.

Иными словами, лямбда-выражения - это не объектно-ориентированный, хотя и лаконичный способ достижения того же, чего достиг бы анонимный класс или, по моему предпочтению, внутренний класс объектно-ориентированным способом.


1
Функциональное программирование может быть полностью ортогональным к объектно-ориентированному программированию (см .: Scala и F #). Это похоже на мнение кого-то, кто просто не любит функциональное программирование в принципе.
KChaloux

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