Pull to refresh

Comments 79

Ура! Наконец-то джависты откроют для себя замечательный мир лямбд и перестанут кричать «лямбды не нужны», потому что в джаве их не было.
В связи с этим, может посоветуете какое нибудь достойное чтиво на эту тему (на русском, английском, немецком)? Буду очень благодарен.

И, да. Мои поздравления, коллеги!
Начать можно с теории /wiki/Замыкание. Как оказалось большинство Java программистов настолько уверовали в исключительность ООП, что просто не знают зачем нужен функциональный подход и что это такое, тем более раньше ничего кроме ООП джавистам и не позволялось.
Ну а потом уже применительно к Java: http://viralpatel.net/blogs/lambda-expressions-java-tutorial/.
Спасибо большое. Java, признаюсь мой первый опыт в ООП языках (php не считаю).
В PHP разве с лямбдами не проще, чем в Java?
Ну лямбды ввели, давно пора, а нормальная поддержка higher-order функций уже есть или по прежнему нужно сооружать чудовищные конструкции с классами?
В этом плане ничего не изменилось. Лямбда, по сути — просто синтаксический сахар для создания анонимного класса с единственным абстрактным методом.

Т.е. higher-order функция объявляется вполне стандартно:
filter(Predicate<? super T> predicate)


Но вот при использовании можно вместо
filter(new Predicate<Object>() {
    public boolean test(Object o) {
        return true;
    }
});

писать
filter(o -> true)


Внутри, на самом деле, неявно создается аноним.
Не соглашусь.

Во-первых, с помощью лямбд можно вполне себе элегантно реализовать функции высшего порядка:
Function<Double, Double> f = Math::sin;
Function<Double, Double> g = f.andThen(Math::sqrt).andThen((x) -> 2 * x);
System.out.println(g.apply(Math.PI / 6));

Во-вторых, никаких анонимных классов нет.
Лямбды компилируются в invokedynamic со вспомогательными методами в том же классе.
Насчёт анонимных классов вы не вполне правы.

Семантически, лямбда — это именно анонимный класс, и обращаться с ней нужно соответственно. Другое дело — как это реализовано. Да, компилятор генерирует invokedynamic для лямбда-функций. А создавать ли анонимный класс JRE решает уже во время исполнения.

Если нужно явно привести лямбду к функциональному интерфейсу (например, чтобы добавить экземпляр этого интерфейса в коллекцию), то создаётся анонимный класс и его экземпляр. Если же лямбда используется только для её вызова (явного или внутри другой лямбды), то приведение к функциональному интерфейсу уже не требуется и, соответственно, анонимный класс не создаётся. Но это уже оптимизация. В общем случае обойтись без анонимных классов не получится.
Семантически лямбда — это «нечто реализуещее требуемый интерфейс», никакого анонимного класса.
В Java реализовывать интерфейс может только класс. И лямбды сведены именно к классам. Новой языковой сущности для лямбд в Java не предусмотрено. Они введены именно как синтаксический сахар. Видимо, для лучшей обратной совместимости.
Сэр, вы не правы.
Это я Вам как разработчик лямбд говорю.
Да? И как тогда реализуется следующий код без создания классов:
Runnable lambda = () -> System.out.println("Hello from lambda");
System.out.println(lambda.getClass());
Анонимный класс появляется во время исполнения, но синтаксическим сахаром ваш пример никак не является. Компилируется это дело в public void метод с названием lambda$N (где N — 0,1,2,3). Анонимный лямбда-метод, если хотите :)
Обратите внимание на вызов getClass(). Сама возможность этого вызова означает наличие класса.

В общем, по моему мнению, как бы авторы не пытались запудрить мозги со своим «лямбда — это не классы» на деле — это всё-таки классы. И не избавятся они от этого факта, покуда в JVM не появится отдельная сущность для лямба-функций. Чего не произойдёт никогда. Рад буду ошибиться.
А класс то и не отрицался.
Отрицается тезис, что это синтаксический сахар для анонимных классов.
А знаете сколько внутри Oracle JVM внутренних классов генерится? уууууу ;)
Факт вообще какого-то класса ни на что не влияет.

И кстати, зафиксировано, что поведение getClass для лямбды неопределено. Сегодня одно возвращаем, в следующем релизе другое. Что хотим то и творим. ;)
Кстати, было бы интересно узнать о деталях реализации из «первых рук». Может, напишете статью по этому поводу? Думаю, не только мне хотелось бы с этим разобраться на более глубоком уровне.
Ну а зачем я 100500 докладов про лямбды сделал? Вот например
http://jeeconf.com/archive/jeeconf-2013/materials/jdk8-lambda/

Вы пожалуйста не путайте 2 вещи: язык и реализацию. В языке (спецификации) четко и ясно :) сказано «нечто реализуещее требуемый интерфейс». ;) То, что первая реализация от Оракла создает некоторые классы на лету еще ничего не значит. И кстати, это нифига не анонимные классы, ибо термин анонимный класс имеет конкретный смысл прописанный в спеке и не нужно мешать понятия. Кстати, как раз анонимные классы — это синтаксический сахар. ;)

При этом у нас на подходе есть несколько «более других» реализаций. МетодХэндлы, там рожь всякая…
Вот допилим и поменяем. ;)
оффтоп: а сколько всего разработчиков работало над лямдами? ну просто любопытно каковы масштабы такого изменения в рамках jvm.
дяденька, посмотрите для интереса, с кем Вы спорите.
Я читаю и слушаю что говорят, а не кто говорит.
зачем этот неуместный пафос? :) Если коротко, то Ваши догадки не верны.
Э-э-э… и… с кем?

Ну, мне на будущее…
с человеком, которые принимал непосредственное участие в разработке лямбд.
Мне нравится ход ваших мыслей, но насчет анонимных классов все равно не соглашусь.
Анонимный класс — вполне определенная конструкция языка, и лямбды таковой не являются.
Может, хотя бы это вас убедит:
  Runnable anonymous = new Runnable() {
      @Override
      public void run() {
          System.out.println("Hello from anonymous");
      }
  };

  Runnable lambda = () -> System.out.println("Hello from lambda");

  System.out.println(anonymous.getClass().isAnonymousClass());  // true
  System.out.println(lambda.getClass().isAnonymousClass());  // false
Интересное замечание. Формально вы, разумеется, правы. Вот только сути это не меняет: класс всё равно создаётся. Пусть его нельзя назвать «анонимным классом» в терминологии Java, но, тем не менее, это анонимный класс, в смысле отсутствия у него имени.
А вот давайте не придумывать терминологию. А получится, что мы будем говорить о разном.
И анонимный класс, кстати, имя то как раз имеет. ;)
1. ответ на Ваш пред. коммент: а я и не говорил, что функции высших порядков нельзя реализовать элегантно, я лишь сказал что в этом месте за исключением очень удобного сахара, не изменилось ничего. А вот сам сахар и добавляет элегантности.

2. Тем не менее, если верить исходникам, то после вызова invokedynamic, внутри бутстрап метода, как раз явно создается класс через asm и загружается через unsafe. Да, это не «старый добрый аноним», а некая более оптимизированная его версия. Но по сути, неявное создание класса все же есть.
Это я смотрел в случае filter, map и пр. Т.е. когда нам обязательно нужен именно экземпляр интерфейса. Если вызывать метод сразу на месте, то, возможно, класс не создается. Не смотрел еще.

Хотя думаю, что все равно будет. В любом случае надо ведь записать что-то в переменную.
1. По данному пункту хотелось бы в первую очередь понять, а что вы имеете ввиду говоря «синтаксический сахар»?
Синтаксический сахар — это некая альтернативная форма записи чего-либо, не меняющая семантику этого «чего-либо». Если же при этом меняется семантика — то это не сахар. И лямбды — это не сахар.
Можно конечно начать юлить, что «да семантика поменялась, а суть не поменялась — значит сахар». Но при таком подходе, мы быстро дойдем до того, что все языки программирования это синтаксический сахар к машинному коду целевой платформы.

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

Ну и конечно факт, что в текущей реализации неявное создание некого класса есть, никто не отрицает. Но в этом смысле и рефлекшн создает классы — значит это сахар к анонимам и многое что другое это сахар. Не нужно всё сводить к сахару, а то у нас сводилка слипнется. ;)
Спасибо за развернутый ответ. С такой формулировкой полностью согласен. Я, действительно, все свои выводы про сахар сделал относительно текущей реализации.
оффтоп
Если честно, то сомневаюсь, что она когда-нибудь поменяется.


Мне просто очень не нравятся заявления в духе «чушь, там invokedynamic», без добавления, что в текущей реализации класс все же создается.

Не нужно всё сводить к сахару, а то у нас сводилка слипнется. ;)

Прошу заметить, я нигде не сказал что сахар — это плохо.
Может я что-то не понимаю, прошу пояснить если так.

Как это сделать через MethodHandle? У нас функция filter принимает Predicate. И тут хочешь не хочешь, но этот Predicate надо иметь. Или имеется ввиду что будет не класс на каждое создание лябды, а единственный класс с полем final MethodHandle? Но, опять же, одним классом не обойтись. Как минимум класс на интерфейс.
Вам важен сам факт генерации класса? А для чего?
«единственный класс с полем final MethodHandle» — ну это уж точно не сахар ;)

Даже не думая я могу набросать/набросить парочку вариантов реализации совсем без класса. ;)
А уж если подууууумать. ;)
Вопрос в другом — а зачем? Метод выливания чайника никто не отменял. ;)
Я Вам задаю один вопрос, Вы мне отвечаете на другой.

Вам важен сам факт генерации класса?
Нет. Мне важен факт понимания реализации.

ну это уж точно не сахар
А где я это говорил?

реализации совсем без класса.
Вот об этом я и спросил. Нам в любом случае нужен объект, реализующий интерфейс Predicate. Как в текущей реализации jvm может быть объект, реализующий интерфейс, но при этом «совсем без класса» я не понимаю. Т.е. мне видится, что это фундаментальное ограничение платформы. Как вы «Даже не думая» можете его обойти?

Метод выливания чайника
Что за метод?
> Я Вам задаю один вопрос, Вы мне отвечаете на другой.
А в чем был вопрос? Вы правильно уловили суть как можно реализовать через MH, тут даже комментировать не нужно.

> Нет. Мне важен факт понимания реализации.
Тогда пока лучше стоит ограничится текущей реализацией — секретный класс на каждую лямбду. ;)

> Нам в любом случае нужен объект, реализующий интерфейс Predicate.
> Как вы «Даже не думая» можете его обойти?
Зачем? Сам Predicate нам не нужен. Нам нужно чтобы у Predicate можно было вызвать test. Соответственно можно везде вместо Predicate использовать MH на нужный метод, ну или даже некий внутренний функциональный указатель.
Это вариант 1.
Вариант 2: total inline в нужном месте с удалением ненужного промежутного мусора.

> фундаментальное ограничение платформы
«Ограничение» — это слово содержащее в себе негативную коннотацию. Я бы на вашем месте не был так категорчен. ;) А вдруг это не ограничение, а наоборот преимущество. ;)

>> Метод выливания чайника
>Что за метод?

Ой. Этому что, уже не учат?

Физику и математику дают задачу. Есть кран с водой, чайник, плита, нужно вскипятить полный чайник воды.
Физик – берет чайник, набирает воду, ставит на плиту, включает плиту, садится ждет.
Математик – берет чайник, набирает воду, ставит на плиту, включает плиту, садится ждет.

Задачу меняют, кран, чайник, плита, но чайник уже с водой наполовину.
Физик – доливает воду, ставит чайник на плиту, включает плиту, садится и ждет.
Математик – выливает воду из чайника и сводит задачу к предыдущей, решение которой уже известно.
Ой. Этому что, уже не учат?
Учат, просто не понял сразу о чем Вы.

А по теме, спасибо, больше вопросов нет.
В корне неверно насчет синтаксического сахара и анонимов — там используется invoke dynamic.
UFO just landed and posted this here
Про invokedynamic я в курсе, выше написано об этом.
Пользуясь случаем передаю привет Гвидо и его «нормальные лямбды не нужны»
Я думаю, Гвидо просто ехидно поинтересуется насчет yield в Java.

Если серьезно, то его подход вполне имеет право на существование. Полноценные однострочные (expression) лямбды в питоне есть. Многострочных нет, но никто не мешает обявить именованную локальную функцию и потом передать её, семантика замыкания сохраняется в полной мере (плюс nonlocal в 3k). Для тех же случаев, когда нужны именно многострочные лямбды, делается более сахар под каждый конкретный паттерн — как, например, with и yield.
как говорят, Java сделала лямбды популярными, не имея их (Java has made lambdas popular by not having them)
Очень рад релизу, мне казалось она должна была выйти в апреле. А что такое Small VM? Кто-нибудь в курсе?

И кстати, я еще пробовал 8 версию до релиза и она действительно в общем была более производительна по сравнению с 7 (где-то на 10-15%).
т.е. можно распространять проекты портабельно (не требуя от конечного пользователя развертывания JAVA_vfibys)?
Не надо забывать по rt.jar, который даже в случае Compact Profile 1 добавит ~14 мб.
Но в целом, да — можно даже слегка поизвращаться и запаковать в exe-файл лаунчера (WinRun4j или Launch4j) саму JRE при помощи Enigma Virtual Box.
Остается только собрать Compact Profile для Windows так как официальных сборок нет.
Все будет выглядеть как обычное приложение, не требующее установленую Java. Выдавать будут только лежащие рядом jar-файлы, которые тоже можно запаковать, но при использовании сжатия (при запаковке в exe) это лишь замедлит старт приложения, к тому же не всегда будет работать.
Да, я знаю, что гнусный извращенец и можно просто воспоользоваться, например Excelsior JET :)
Т.е. они сделали Small VM без учета Runtime библиотеки?
Просто оптимизировали сборку libjvm.so — по размеру результируемого файла, а не по быстродействию, с оговоркой, что быстродействие не должно упасть более чем на 5%.
Ну эти «стандартные средства» от JavaFX появились не так давно, да и набор функций отличается от тех, что предлагают лаунчеры. Надо смотреть что конкретно нужно.
UFO just landed and posted this here
Ещё Scala 2.11 на подходе. Вот так праздник.
Новая версия — это хорошо, и рад за джавистов, джаверов и прочих любителей кофе, но очень надеюсь, что не будет как в прошлый раз, при переходе с 6 на 7.
К самому Ораклу претензий нет, но у нас в Казахстане куча сервисов электронного правительства, госзакупки, налоговая и прочее работает на Java. И вот при выходе 7-ки народ на местах обновился, и половина сервисов превратилась в тыкву.
Повторюсь — к самому Ораклу претензий особых нет, все из-за криворуких программеров наших сервисов, но апгрейда у пользователей жду с некоторой опаской.
Как так? Ведь обратная совместимость — краеугольный камень JavaVM. Что можно было такого наворотить, из-за чего перестало работать на новых версиях? Примеры можете привести?
Да легко, далеко ходить не будем. Несколько месяцев назад выходил новый апдейт (не с 6 на 7, а что-то там с 41 на 45, не помню точно цифры). При установке предупреждала — со следующего релиза неподписанные апплеты запускаться при настройках по умолчанию не будут.
Прошел месяц или два. вышел новый апдейт, о котором предупреждали.
И что вы думаете — пришлось по всем юзерам пройтись и настроить уровень безопасности минимальный и добавить сайты с неподписанными апплетами в список исключений.
Это просто один пример, навскидку.
При переходе с 6 на 7 вообще было здорово — один из банк-клиентов отказался работать с 7-кой, а вот кабинет налогоплательщика очень быстро отказался работать с 6-й. Пару месяцев пришлось подождать, пока они между собой не синхронизировались.
[sarcasm]Ну оно понятно, ведь для наших джава-разработчиков выход каждой новой версии абсолютно неожиданный, а тестовых и бета версий, чтобы проверить свои апплеты, просто не существует[/sarcasm]
UFO just landed and posted this here
Под разработчиками я имею в виду всех тех, кто предоставляет нам и, хуже того, обязывает нас использовать сырой неоттестированный продукт. Меня мало волнуют их внутренние самоназвания и бизнес-процессы. Меня волнует только то, что после апгрейда возможны проблемы. А если они возможны, значит, они будут.
Я поступлю нескромно и дам ссылку на своё выступление:


Там есть про несовместимости как про управление рисками.
Ура!

Ходят слухи, что 3 апреля в питерском офисе Oracle будет большая Java 8 Launch Party со свободным входом. Будут подробности — напишу.
Еще годик подождем, видимо)
Не один годик я, бы сказал…
Простой вопрос: будут ли исходники, написанные на Java 8, компилироваться в target Java 7/6? В свое время переход с 1.4 -> 5.0 был достаточно болезненным по причине несовместимости байткода и затянулся на 5 лет.
Такой возможности нет, если хотите использовать новые фичи языка. Появились некоторые новые инструкции в байткоде JVM, которых нет даже в JVM 1.7
Как минимум Stream Style, это опять же к докладу Walrus, LongAdder, AES, да и много другого вкусного по ссылке в топике.
наверное не совсем по теме, но можно ли как то уже под винду в виде zip скачать? ( portableapps.com/apps/utilities/java_portable отстает да)
и совсем не по теме: почему все же у оракла (вроде начиная с 1.7) больше нет этого вариант скачивания? есть еще разработчики без админских прав на тачках, да ))
При помощи 7-zip и лежащего внутри unpack200 можно из exe получить всё, что нужно. Либо перенести с другой машины. Стандартного варианта скачивания увы нет.
UFO just landed and posted this here
Ну, наконец теперь и в Java «шаблоны проектирования» начнут постепенно заменяться соответствующими им рядовыми примерами из учебников по функциональному программированию…
Sign up to leave a comment.

Articles