любой анонимный класс можно заменить на лямбду

Лямбда-выражения в Java

Поддержка лямбда-выражений, реализованная в Java 8, стала одним из наиболее значимых нововведений за последнее время. Будучи упрощённой записью анонимных классов, лямбды позволяют писать более лаконичный код при работе со Stream или Optional. Лямбда-выражения часто используются как совместно со многими API стандартной библиотеки Java, так и со сторонними API, среди которых JavaFX, реактивные стримы и т.д.

Лямбды и функциональные интерфейсы

Лямбда-выражение или просто лямбда в Java — упрощённая запись анонимного класса, реализующего функциональный интерфейс.

Функциональный интерфейс в Java — интерфейс, в котором объявлен только один абстрактный метод. Однако, методов по умолчанию (default) такой интерфейс может содержать сколько угодно, что можно видеть на примере java.util.function.Function. Функциональный интерфейс может быть отмечен аннотацией @FunctionalInterface, но это не обязательное условие, так как JVM считает функциональным любой интерфейс с одним абстрактным методом.

Пример простого функционального интерфейса:

Структура лямбда-выражения

Сигнатура лямбда-выражения соответствует сигнатуре абстрактного метода реализуемого функционального интерфейса. Можно даже сказать, что лямбда-выражение является реализацией абстрактного метода этого функционального интерфейса. Главное отличие сигнатуры лямбда-выражения от сигнатуры метода в том, что она состоит только из двух частей: списка аргументов и тела, разделённых при помощи «->». Возвращаемый тип и возможные выбрасываемые исключения JVM берёт из интерфейса.

Типы аргументов лямбда-выражения опциональны, так как они декларируются интерфейсом, но при использовании обобщений (дженериков) с extends/super может возникнуть необходимость в указании конкретных типов аргументов. При этом стоит отметить, что типы либо указываются для всех аргументов, либо не указываются вообще. Это же касается и использования var, введённой в Java 11. Всё это можно свести к такому правилу: все аргументы объявляются либо с типами, либо с var, либо без них.

Если у лямбда-выражения всего один аргумент, и для него не требуется объявление типа или var, то круглые скобки можно опустить. В остальных случаях, в том числе если лямбда не принимает никаких аргументов, скобки нельзя опустить.

Аналогичная ситуация и с телом лямбда-выражений: если оно состоит только из одной строки, то фигурные скобки, точку с запятой (;) и директиву return можно тоже опустить.

В качестве тела лямбда-выражения может использоваться ссылка на метод.

Создание лямбда-выражений

Допустим, нам нужна реализация CarFilter, описанного выше, которая проверяла бы, что автомобиль выпущен не раньше 2010 года. Если мы будем использовать анонимный класс, то создание объекта CarFilter будет выглядеть примерно следующим образом:

Но мы можем описать объект CarFilter при помощи лямбда-выражения:

Однако, эту запись можно сделать ещё меньше:

Согласитесь, что такая запись зачительно меньше и лаконичнее, чем использование анонимного класса.

Применение лямбда-выражений

Допустим у нас есть задача написать метод, выводящий из полученного списка автомобили, у которых тип кузова (body) — STATION_WAGON и мощность (power) — больше 200 л.с.

Скорее всего, мы напишем что-то вроде:

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

В этом случае логично было бы использовать сразу два функциональных интерфейса: java.util.function.Predicate — для фильтрации и java.util.function.Consumer — для действия, применяемого к подходящим объектам.

java.util.function.Predicate декларирует абстрактный метод test, который принимает объект и возвращает значение типа boolean в зависимости от соответствия переданного объекта требуемым критериям.

java.util.function.Consumer декларирует абстрактный метод accept, который принимает объект и выполняет над ним требуемые действия.

Метод printCars превратится во что-то похожее на следующий метод:

И первоначальную задачу вывести из полученного списка автомобили, у которых тип кузова (body) — STATION_WAGON и мощность (power) — больше 200 л.с. мы решили бы следующим вызовом метода processCars с использованием лямбда-выражений:

Или при помощи анонимных классов:

Вариант вызова метода processCars с использованием лямбда-выражений значительно компактнее.

Лямбды, анонимные классы и обычные классы

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

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

Но в большинстве случаев, там где можно применять лямбда-выражения, например в Stream, Optional или CompletableFuture, логичнее применять именно лямбды.

Источник

Lambda-выражения в Java

Привет, Хабр! Представляю вашему вниманию перевод статьи «Java Lambda Expressions» автора www.programiz.com.

Введение

В этой статье, с помощью примеров, мы изучим lambda-выражения в Java, их использование с функциональными интерфейсами, параметризированными функциональными интерфейсами и Stream API.

Лямбда выражения были добавлены в Java 8. Их основная цель – повысить читабельность и уменьшить количество кода.

Но, прежде чем перейти к лямбдам, нам необходимо понимать функциональные интерфейсы.

Что же такое функциональный интерфейс?

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

Например, интерфейс Runnable из пакета java.lang является функциональным, потому, что он содержит только один метод run().

Пример 1: объявление функционального интерфейса в java

В приведенном выше примере, интерфейс MyInterface имеет только один абстрактный метод getValue(). Значит, этот интерфейс — функциональный.

Здесь мы использовали аннотацию FunctionalInterface, которая помогает понять компилятору, что интерфейс функциональный. Следовательно, не позволяет иметь более одного абстрактного метода. Тем не менее, мы можем её опустить.

В Java 7, функциональные интерфейсы рассматривались как Single Abstract Methods (SAM). SAM обычно реализовывались с помощью анонимных классов.

Пример 2: реализация SAM с помощью анонимного класса в java

В этом примере, мы принимаем анонимный класс для вызова метода. Это помогало писать программы с меньшим количеством строк кода в Java 7. Однако, синтаксис оставался достаточно сложным и громоздким.

Читайте также:  на чем работает погрузчик

Java 8 расширила возможности SAM, сделав шаг вперед. Как мы знаем, функциональный интерфейс содержит только один метод, следовательно, нам не нужно указывать название метода при передаче его в качестве аргумента. Именно это и позволяет нам lambda-выражения.

Введение в лямбда-выражения

Лямбда-выражения, по сути, это анонимный класс или метод. Лямбда-выражение не выполняется само по себе. Вместо этого, оно используется для реализации метода, определенного в функциональном интерфейсе.

Как записать лямбда-выражение в Java?

В Java, лямбда-выражения имеют следующий синтаксис:

Здесь мы использовали новый оператор (->) — лямбда-оператор. Возможно, синтаксис кажется немного сложным. Давайте разберем пару примеров.

Предположим, у нас есть такой метод:

Мы можем записать его, используя лямбда, как:

Этот метод не имеет никаких параметров. Следовательно, левая часть выражения содержит пустые скобки. Правая сторона – тело лямбда-выражения, которое определяет его действие. В нашем случае, возвращается значение 3.1415.

Типы лямбда-выражений

В Java, тело лямбды может быть двух типов.

2. Блочные (многострочные)

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

Примечание: многострочные лямбда-выражения, всегда должны иметь оператор return, в отличии от однострочных.

Пример 3: лямбда-выражение

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

Как говорилось ранее, лямбда-выражение не выполняется само собой. Скорее, оно формирует реализацию абстрактного метода, объявленного в функциональном интерфейсе.

И так, для начала, нам необходимо описать функциональный интерфейс.

Лямбда-выражения с параметрами

До этого момента, мы создавали лямбда-выражения без каких-либо параметров. Однако, как и методы, лямбды могут иметь параметры.

В этом примере, переменная n внутри скобок является параметром, переданном в лямбда-выражение. Тело лямбды принимает параметр и проверяет его на четность.

Пример 4: использование лямбда-выражения с параметрами

Параметризированный функциональный интерфейс

До этого момента, мы использовали функциональные интерфейсы, которые принимали только один тип значения. Например:

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

Пример 5: параметризированный интерфейс и лямбда-выражения

В этом примере, мы создали параметризированный функциональный интерфейс GenericInterface, который содержит параметризированный метод func().

Затем, внутри класса Main:

Лямбда-выражения и Stream API

В JDK8 добавлен новый пакет java.util.stream, который позволяет java-разработчикам выполнять такие операции, как поиск, фильтрация, сопоставление, объединение или манипулирование коллекциями, к примеру Lists.

Например, у нас есть поток данных (в нашем случае список строк), где каждая строка содержит название страны и ее город. Теперь мы можем обработать этот поток данных и выбрать только города Непала.

Для этого мы можем использовать комбинацию Stream API и лямбда-выражений.

Пример 6: использование лямбд в Stream API

В приведенном выше примере обратите внимание на это выражение:

Здесь мы используем такие методы, как filter(), map(), forEach() из Stream API, которые могут принимать лямбды в качестве параметра.

Также, мы можем описать собственные выражения на основе синтаксиса, описанного выше. Это позволит нам уменьшить количество строк кода.

Источник

Анонимный класс можно заменить лямбдой?

Пока я пишу код в intellij, он продолжает появляться, говорит

Анонимный класс можно заменить лямбдой?

Так что это значит? что такое лямбда-выражение и как анонимный класс можно заменить лямбда-выражением?

2 ответа

Анонимный новый можно заменить на лямбда?

Это не на 100% правильно, анонимный класс для интерфейсов, имеющих один абстрактный метод, можно заменить лямбда-выражением (которое называется функциональным интерфейсом)

Лямбда-выражения

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

Функциональный интерфейс имеет ровно один абстрактный метод. Поскольку методы по умолчанию имеют реализацию, они не являются абстрактными. Если интерфейс объявляет абстрактный метод, переопределяющий один из общедоступных методов java.lang.Object, это также не засчитывается в счетчик абстрактных методов интерфейса, поскольку любая реализация интерфейса будет иметь реализацию из java.lang.Object или где-то еще.

Ваш код можно изменить на этот:

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

Источник

Лямбды и анонимные классы: кто больше жрёт

По мотивам недавних обсуждений здесь захотелось более широко взглянуть на вопрос о том, кто больше кушает — новомодные хипстерские лямбды или старые проверенные анонимные классы. Давайте устроим словесную перепалку между ними и посмотрим, кто выиграет. Как с любым добротным холиваром, даже если не удастся выяснить победителя, можно узнать много нового для себя.

Первый раунд. Пространство на экране.

Лямбда-кун: хе. Хе-хе-хе. Нет, это несерьёзно. Ну как может сравниться вот это убожество:

С вот этой красотой:

Анон-сан: ну-ну, молодой человек, незачем так выражаться. В наши дни никого не волнует, что там в файле на самом деле. Достаточно взять хорошую IDE и разница уже практически незаметна.

Вот как выглядит анонимный класс:

А вот лямбда:

Анон-сан: при этом меня можно развернуть и посмотреть, что я такое на самом деле, а вот что вы такое на самом деле — большая загадка.

Второй раунд. Пространство на диске.

Лямбда-кун: кх-хм… Нет, это, конечно, нечестно. Ну ладно. Но на диске-то я занимаю меньше. Возьмём простой класс:

А с тобой будет вот что:

52 байта против 126! Каково, а?

Анон-сан: ну с байтами исходников я соглашусь, хотя кого они волнуют. А если скомпилировать?

Читайте также:  листовой салат при подагре можно или нет

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

Лямбда-кун: эээ, как это получилось? Не могло быть, я же легковеснее! Ну-ка, а с отладочной информацией сколько будет? Все же с ней компилируют.

Лямбда-кун: вооот, уже я выигрываю! С отладочной информацией 957 байт, а если заменить на анонимные классы, будет ажно 1351 байт в трёх файлах. Даже без отладочной информации я выигрываю. А ведь могут и другие константы эффективно переиспользоваться! Любые поля, методы, классы, используемые внутри лямбд. Если они используются в нескольких лямбдах или в лямбде и вокруг неё, то схлопнутся в одну константу. А с анонимными классами в каждом будет копия. То-то же!

Третий раунд. Классы в рантайме.

Анон-сан: видимо, тут мне придётся уступить. Если лямбд много, то вы действительно компактнее в скомпилированном виде. Однако более интересно, что происходит в рантайме, в памяти виртуальной машины. Пусть для тебя нету анонимного класса на диске, но точно такой же класс, а то и больший, будет сгенерирован при запуске и сожрёт все те же ресурсы.

Лямбда-кун: а вот и не те же! Я же легковеснее! Там наверняка сгенерируется маленький компактный классик, который не содержит всякой ненужной ерунды. Да и как ты это проверишь? Оно ж всё в рантайме в памяти!

Лямбда-кун: ага, только пока лямбду ни разу не используешь, рантайм-представление не будет сгенерировано вообще, а анонимные классы существуют всегда, даже если ни разу не пригодились!

Анон-сан: нет никакой разницы. Даже наоборот, разница не в вашу пользу. Анонимный класс существует всегда на диске, но он не будет загружен в память, пока не используется. Рантайм-представление лямбды, конечно, сгенерировано не будет, однако её тело в виде приватного синтетического метода загружается фактически вместе с классом, в котором она объявлена. Даже если тело ни разу не используется, оно память отъест. Впрочем, к этому вопросу вернёмся позднее. Посмотрим сперва, что происходит, если лямбда используется. Для этого нам потребуется немного модифицировать программу:

Лямбда-кун: ну мы же договорились, что одной лямбдой меряться нечестно. Давай добавим вторую.

Анон-сан: хм, а в чём разница?

Лямбда-кун: а скомпилируй, и увидишь. У меня-то как раз разницы нет, сгенерированные в рантайме классы весят столько же. А у тебя каждый байт на 40 потолстел. Теперь на десяти лямбдах я кушаю меньше (5290 байт против 4995). Уже даже на шести я тебя опережаю!

Анон-сан: по-моему, наша игра уже перетекает в нечестную плоскость. Так что вот, с вашего позволения, ответный удар: замыкание на переменных. Принимаю ваше условие и остаюсь внутри метода. Но захватим-ка переменную:

Лямбда-кун: ну тут-то разницы быть не должно. У тебя компилятором генерируется синтетическое поле и конструктор с одним параметром, который это поле инициализирует. У меня примерно то же самое будет создано в рантайме. Какой-то примерно такой класс генерируется и для тебя, и для меня:

Анон-сан: такой, да не такой. Пробуем. Одна лямбда: 1493 байта, один анонимный класс: 1006 байт. Десять лямбд: 6803 байта, десять анонимных классов: 6039 байт. Двадцать лямбд: 12743 байта, двадцать анонимных классов: 11669 байт. Разрыв постоянно увеличивается! Тут хоть тысяча лямбд, а вам меня не догнать.

Лямбда-кун: ээ… Так. Ну-ка, декомпилируем. Это ещё что за ерунда? Какой-то фабричный метод? Глупость какая-то. Помимо конструктора мне ещё зачем-то добавляют метод вида static IntSupplier get$Lambda(int i) < return new Test$1(i);>. Бред какой-то, зачем?

Анон-сан: не бред, а производительность. Когда-то в незапамятные времена Walrus исправил скорость инстанциирования лямбд в интерпретаторе (JDK-8023984). Фабричный метод оказался быстрее, чем конструктор. Заметьте, молодой человек, со мной таких странных проблем не возникает, у меня всё быстро и так.

Лямбда-кун: вот же глупость-то! Нет чтобы допилить свои методхэндлы до ума, они костыли лепят… Интересно, может уже с тех пор допилили и этот метод не нужен стал.

Анон-сан: как знать, как знать.

Лямбда-кун: выходит, что нет. Лямбде позволительно, чтобы для неё это не работало!

Анон-сан: однако хотя эта строчка отъест место, не верится мне, что сильно много против вашего фабричного метода.

Лямбда-кун: а ты не верь, а проверь. Теперь всего три лямбды кушают меньше, чем три анонимных класса (2963 против 3034 байта) и с каждой новой строчкой ты всё больше проигрываешь! Каждый анонимный класс кушает на 270 байт больше соответствующей лямбды. И это с учётом того, что у меня лишний фабричный метод!

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

Источник

Разбираем лямбда-выражения в Java

От переводчика: LambdaMetafactory, пожалуй, один из самых недооценённых механизмов Java 8. Мы открыли его для себя совсем недавно, но уже по достоинству оценили его возможности. В версии 7.0 фреймворка CUBA улучшена производительность за счет отказа от рефлективных вызовов в пользу генерации лямбда выражений. Одно из применений этого механизма в нашем фреймворке — привязка обработчиков событий приложения по аннотациям, часто встречающаяся задача, аналог EventListener из Spring. Мы считаем, что знание принципов работы LambdaFactory может быть полезно во многих Java приложениях, и спешим поделиться с вами этим переводом.

В этой статье мы покажем несколько малоизвестных хитростей при работе с лямбда-выражениями в Java 8 и ограничения этих выражений. Целевая аудитория статьи — senior Java разработчики, исследователи и разработчики инструментария. Будет использоваться только публичный Java API без com.sun.* и других внутренних классов, поэтому код переносим между разными реализациями JVM.

Короткое предисловие

Например, у нас есть следующий код:

Этот код будет преобразован компилятором Java во что-то похожее на:

Инструкция invokedynamic может быть примерно представлена как вот такой Java код:

Читайте также:  недопустимая сессия попробуйте перезапустить игру что делать

В Oracle JRE 8 metafactory динамически генерирует Java класс, используя ObjectWeb Asm, который и создает класс-реализацию функционального интерфейса. К созданному классу могут быть добавлены дополнительные поля, если лямбда-выражение захватывает внешние переменные. Этот похоже на анонимные классы Java, но есть следующие отличия:

Реализация metafactory зависит от вендора JVM и от версии

Конечно же, инструкция invokedynamic используется не только для лямбда-выражений в Java. В основном, она применяется при выполнении динамических языков в среде JVM. Движок Nashorn для исполнения JavaScript, который встроен в Java, интенсивно использует эту инструкцию.

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

Трюки с лямбда-выражениями

В этом разделе мы покажем, как строить динамические конструкции из лямбд для использования в ежедневных задачах.

Проверяемые исключения и лямбды

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

А что, если вам нужно использовать код с проверяемыми исключениями внутри лямбда-выражений в сочетании с Java Streams? Например, нужно преобразовать список строк в список URL как здесь:

В конструкторе URL(String) объявлено проверяемое исключение, таким образом, он не может быть использован напрямую в виде ссылки на метод в классе Functiion.

Вы скажете: «Нет, возможно, если использовать вот такую хитрость»:

Это грязный хак. И вот почему:

Проблема может быть решена более «легальным» способом, с использованием знания следующих фактов:

Решение — обернуть метод Callable.call в метод без секции throws :

Сначала нам нужно объявить функциональный интерфейс, в котором нет секции throws
но который сможет делегировать вызов к Callable.call :

Третье — напишем вспомогательный метод, который вызывает Callable.call без объявления исключений:

Теперь можно переписать stream без всяких проблем с проверяемыми исключениями:

Этот код скомпилируется без проблем, потому что в callUnchecked нет объявления проверяемых исключений. Более того, вызов этого метода может быть заинлайнен при помощи мономорфного инлайн кэширования, потому что это только один класс во всей JVM, который реализует интерфейс SilentOnvoker

Если реализация Callable.call выкинет исключение во время выполнения, то оно будет перехвачено вызывающей функцией без всяких проблем:

Несмотря на возможности этого метода, нужно всегда помнить про следующую рекомендацию:

Скрывайте проверяемые исключения при помощи callUnchecked только если уверены, что вызываемый код не выкинет никаких исключений

Следующий пример показывает пример такого подхода:

Полная реализация этого метода находится здесь, это часть проекта с открытым кодом SNAMP.

Работаем с Getters и Setters

Этот раздел будет полезен тем, кто пишет сериализацию/десериализацию для различных форматов данных, таких как JSON, Thrift и т.д. Более того, он может быть довольно полезен, если ваш код сильно полагается на рефлексию для Getters и Setters в JavaBeans.

Первый шаг: необходимо создать кэш для getters и setters. Класс Method из Reflection API представляет реальный getter или setter и используется в качестве ключа.
Значение кэша — динамически сконструированный функциональный интерфейс для определенного getter’а или setter’а.

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

Автоматическое приведение типов между аргументами типа Object в функциональных интерфейсах (после стирания типов) и реальными типами аргументов и возвращамого значения достигается при помощи разницы между samMethodType и instantiatedMethodType (третий и пятый аргументы метода metafactory, соответственно). Тип созданного экземпляра метода — это и есть специализация метода, который предоставляет реализацию лямбда-выражения.

В-третьих, создадим фасад для этих фабрик с поддержкой кэширования:

А теперь — время тестировать код:

Этот подход с закэшированными getters и setters можно эффективно использовать в библиотеках для сериализации/десериализации (таких, как Jackson), которые используют getters и setters во время сериализации и десериализации.

Вызовы функциональных интерфейсов с динамически сгенерированными реализациями с использованием LambdaMetaFactory значительно быстрее, чем вызовы через Java Reflection API

Полную версию кода можно найти здесь, это часть библиотеки SNAMP.

Ограничения и баги

В этом разделе мы рассмотрим некоторые баги и ограничения, связанные с лямбда-выражениями в компиляторе Java и JVM. Все эти ограничения можно воспроизвести в OpenJDK и Oracle JDK с javac версии 1.8.0_131 для Windows и Linux.

Создание лямбда-выражений из обработчиков методов

Этот код эквивалентен:

Но что, если мы заменим обработчик метода, который указывает на getValue на обработчик, который представляет getter поля:

Этот код должен, ожидаемо, работать, потому что findGetter возвращает обработчик, который указывает на getter поля и у него правильная сигнатура. Но, если вы запустите этот код, то увидите следующее исключение:

Что интересно, getter для поля работает нормально, если будем использовать MethodHandleProxies:

Нужно отметить, что MethodHandleProxies — не очень хороший способ для динамического создания лямбда-выражений, потому что этот класс просто оборачивает MethodHandle в прокси-класс и делегирует вызов InvocationHandler.invoke методу MethodHandle.invokeWithArguments. Этот подход использует Java Reflection и работает очень медленно.

Как было показано ранее, не все обработчики методов могут быть использованы для создания лямбда-выражений во время выполнения кода.

Только несколько типов обработчиков методов могут быть использованы для динамического создания лямбда-выражений

Generic исключения

Но, если мы заменим лямбда-выражение анонимным классом, то код скомпилируется:

Вывод типов для generic исключений не работет корректно в сочетании с лямбда-выражениями

Ограничения типов параметризации

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

Этот код абсолютно корректный и успешно компилируется. Класс MutableInteger удовлетворяет ограничениям обобщенного типа T:

Но код упадет с исключением во время выполнения:

Этот пример демонстрирует некорректный вывод типов в компиляторе и среде исполнения.

Обработка нескольких ограничений типов generic параметров в сочетании с использованием лямбда-выражений во время компиляции и выполнения — неконсистентна

Источник

Строительный портал