Лямбда-вирази в Java 8: як використовувати, приклади


Опубликованно 29.11.2018 00:07

Лямбда-вирази в Java 8: як використовувати, приклади

Кожен розробник повинен навчитися використовувати нові функції програмування, особливо тепер, коли 8-я версія досягла максимального піку використання. Лямбда-вирази - це нова і важлива функція, включена в Java 8. Вона забезпечує чіткий і стислий спосіб подання методу, який покращує Collection бібліотеки, спрощуючи ітерацію, фільтрацію та вилучення даних. Нові можливості паралелізму покращують продуктивність багатоядерних середовищах. Принцип лямбда-виразів

Кілька років тому фахівцями обговорювалися розширення мови Java для функціонального програмування. У 2009 році стало ясно, що Java без lambdas буде виглядати застарілим в порівнянні з іншими мовами програмування, оскільки сучасне багатопроцесорне і багатоядерне обладнання вимагає простої підтримки для розпаралелювання програм. Ось навіщо потрібні лямбда-вирази.

В Java всі колекції мають метод forEach, успадкований від свого супер-інтерфейсу Iterable. Він існує з часів Java 5, був розширений до 8-ї версії. Метод forEach виконує перебору всіх елементів колекції і застосовує функцію до кожного елементу.

Лямбда-вираз являє собою анонімну функцію, що забезпечує параметризацію поведінки. Вона складається зі списку параметрів повернення і винятків, які вибираються. Примірник лямбда-вирази Java може бути призначений будь Iterable, відповідного визначення за умови, що він є функціональним.

Важливі моменти для розуміння лямбда-виразу: Елемент зліва від стрілки (->) - це параметри лямбда. У цьому випадку вхідний параметр визначається, як String param. Праворуч від стрілки (->) - тіло лямбды. Тіло - це місце, де відбувається фактична обробка лямбда, тобто визначає її логіку. Зазвичай лямбда-вираз Java має просту логіку.

Синтаксичних опції для списку параметрів

Існує кілька синтаксичних опцій для списку параметрів. Ось спрощена версія синтаксису лямбда-виразу: LambdaExpression. LambdaParameters '->' LambdaBody LambdaParameters. Identifier '(' ParameterList ')' LambdaBody. Expression Block.

Список параметрів являє собою список, розділений комами, в круглих дужках, або один ідентифікатор без дужок. Якщо використовується скобочный список, потрібно вирішити, чи буде прописаний тип значення для всіх або якщо він не буде вказано, тоді він автоматично визначиться компілятором. Ось кілька прикладів лямбда-виразів Java:

(int x) -> x + 1

Список параметрів з єдиним параметром з явною специфікацією типу

int x -> x + 1

Невірно, якщо вказаний тип значення, використовують круглі дужки

(x) -> x + 1

Список параметрів з єдиним параметром без явної специфікації типу, компілятор виводить відсутній тип значення з контексту

x -> x + 1

Список параметрів з єдиним параметром без явної специфікації типу. У цьому випадку опускають круглі дужки

(int x, int y) -> x + y

Список параметрів з двома параметрами n з явною специфікацією типу

int x, int y -> x + y

Невірно, якщо зазначений тип параметра, то повинні використовуватися круглі дужки

(x, y) -> x + y

Список параметрів з двома параметрами без явної специфікації типу

x, y -> x + y

Невірно, для більш ніж одного параметра необхідно використовувати дужки

(x, int y) -> x + y

Невірно, не можна змішувати з типом і без нього. Або всі мають явну специфікацію типу, або ні

() -> 42

Список може бути порожнім

Тіло лямбды являє собою один вираз, або список виразів у дужках. Ось кілька прикладів лямбда-виразів Java:

() -> System.gc ()

Тіло, що складається з одиночного вираження

(String [] args)

-> (args! = null) ? args.length : 0

Оператор також повертає одне вираз

(String [] args)

-> {if (args! = null)

return args.length ;

ще

return 0 ;

}

Тут використовуються фігурні дужки, тому що це інструкція, а не вираз

(int x) -> x + 1

Тіло, при передачі лямбда-виразів.

(int x) -> return x + 1

Невірно, з поверненням починається твердження, і лямбда відсутня, return повинен закінчуватися крапкою з комою і перебувати в дужках

(int x) -> { return x + 1 ; }

Правильно

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

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

Для всіх функціональних інтерфейсів рекомендується мати інформативну анотацію. Це не тільки чітко передає його мета, але також дозволяє компілятору генерувати помилку, якщо анотований інтерфейс не задовольняє умовам SAM. Інтерфейс з Single Abstract Method є функціональним, і його реалізація може розглядатися, перед тим як використовувати лямбда-вирази в Java 8.

Методи спочатку не абстрактні і не враховуються, а інтерфейс може мати кілька методів за замовчуванням. Можна спостерігати це, дивлячись на документацію Function. Самий простий і загальний випадок лямбда - це функціональний інтерфейс з методом, який отримує одне значення, повертаючи інше. Ця функція одного аргументу представлена інтерфейсом Function, який параметризуется типами аргументів і возвращаемыми значеннями. Приклади-лямбда виразу Java: 1; public interface Function { ... }. Анонімний внутрішній клас

В Java анонімні внутрішні класи надають спосіб реалізації класів, що зустрічаються в програмі лише один раз. Наприклад, у стандартному додатку Swing або JavaFX потрібно кілька обробників подій для клавіатури і миші. Замість того щоб писати окремий клас для кожної події, прописують загальну команду. Створюючи клас на місці, де це необхідно, код стає читати набагато простіше.

Не можна правильно зрозуміти, що таке лямбда виразу, не розглянувши функціональні інтерфейси. Використання їх з анонімними внутрішніми класами є загальним шаблоном в Java. На додаток до EventListener класами, такі інтерфейси, як Runnable, Comparator використовуються аналогічним чином. Тому функціональні інтерфейси використовуються конкретно з лямбда-виразів.

Вираз анонімної функції

В основному, Lambda Expression - коротке анонімної функції, яка може бути передана. Таким чином, вона володіє наступними властивостями:

Анонімність, тому що у неї немає, як зазвичай, явного імені. Функціональність, її можна розглядати, як функцію, тому що лямбда не пов'язана з певним класом, як метод. Але аналогічно методу, лямбда має список параметрів, тіло, тип повернення і список винятків, які можуть бути вибрані. Прохід, лямбда вираз може передаватися, як аргумент методу або зберігатися змінної. Стислість, не потрібно писати багато шаблонів, як для анонімних класів. Технічно вираження Lambda дозволяють робити те, що не робили до Java 8, крім того тепер не потрібно писати незграбний код, щоб використовувати параметризацію поведінки. Список параметрів. В цьому випадку він відображає параметри методу порівняння компаратора - два об'єкта Apple. Стрілка « ->» відділяє список параметрів від тіла лямбда. Вираз вважається її обчислене значення лямбда. Синтаксис вираження

Лямбда-вирази визначають об'ємність анонімних внутрішніх класів шляхом перетворення 5 рядків коду в один оператор. Це просте горизонтальне рішення дозволяє «вертикальну проблему», представлену внутрішніми класами. Лямбда-вираз складається з 3 частин.

Тіло може бути або окремим вираженням, або блоком оператора. У формі вираження тіло просто оцінюється і повертається. У блочній формі тіло оцінюється, як тіло методу, а оператор return повертає викликає стороні анонімного методу. На верхньому рівні break continue ключові слова є незаконними, але можна усередині циклів. Якщо тіло виробляє результат, кожен шлях управління повинен повертати що-небудь або генерувати виключення.

Наприклад: (int x, int y) -> x + y; () -> 42; (String s) -> {System.out.println (s); }.

Перший вираз приймає два цілочисельних аргументів, названих x і y, і використовує форму вираження для повернення x + y. Друге не приймає аргументів і використовує форму для повернення цілого числа 42. Третє вираз приймає рядок і використовує форму блоку для друку рядка на консолі і нічого не повертає. Захист змінних об'єкта від мутації

Доступ до змінної в лямбда-вирази призведе до помилки часу компіляції. Але це не означає, що користувач повинен відзначати кожну цільову змінну як final. В відповідності з концепцією «фактично остаточний» компілятор розглядає кожну змінну, як остаточну, якщо вона призначається тільки один раз.

Безпечно використовувати такі змінні всередині lambdas, тому що компілятор буде контролювати свій стан і запускати помилки часу компіляції одразу після будь-якої спроби їх змінити. Цей підхід повинен спростити процес виконання лямбда-виконання. Однією з основних цілей лямбда є використання в паралельних обчисленнях - це означає, що вони дійсно корисні, коли справа стосується безпеки потоків.

Ефективна остаточна парадигма допомагає у багатьох, але не в кожному випадку. Lambdas не може змінити значення об'єкта охоплює області. Але у випадку змінюваних змінних стан об'єкта може бути змінено всередині лямбда-виразів. Цей код є законним, оскільки повна мінлива залишається, фактично, остаточною. Захоплення змінної примірника

Лямбда вираз також може захоплювати змінну примірника в об'єкті, який створює лямбда. Ось приклад, який показує процес.

Потрібно звернути увагу на посилання this.name всередині лямбда-тіла. Це фіксує name змінну примірника EventConsumerImpl об'єкта. Можна навіть змінити значення змінної примірника після її захоплення, і значення буде відображено всередині лямбда. Семантика this фактично є однією з областей, де Java lambdas відрізняється від анонімних реалізацій інтерфейсів. Реалізація анонімного інтерфейсу може мати свої власні змінні екземпляра, на які посилається this посилання. Тим не менш, лямбда не може мати свої власні змінні екземпляра, тому this завжди вказує на охоплює об'єкт.

Захоплення статичної змінної Ямба-вираз Java також може захоплювати статичні змінні. Це не дивно, так як статичні змінні доступні скрізь, де є додаток Java. Значення статичної змінної також може змінитися після того, як лямбда її захопила. Клас в першу чергу служить для того, щоб показати, що лямбда може звертатися до статичних змінних. Посилання на методи

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

Оскільки тіло лямбда складається лише з одного твердження, можна фактично опустити додаються { } дужки. Крім того, оскільки для цього методу існує тільки один параметр, можна опустити додаються ( ) дужки навколо параметра. Ось як виглядає підсумкова заява лямбда: MyPrinter myPrinter = s -> System.out.println (s);

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

Ось як виглядає посилання на лямбда-метод: MyPrinter myPrinter = System.out :: println.

Потрібно звертати особливу увагу на подвійні двокрапки ::. Цей сигнал компілятора Java є посиланням на метод. Зазначений метод - це те, що відбувається після подвійних двокрапки. Незалежно від класу або об'єкта, який володіє посилальних методом. Поширені приклади використання

Біжить Лямбда.

В обох випадках параметр не передається і повертається. Runnable лямбда - вираз, який використовує формат блоку, перетворює п'ять рядків коду в одній інструкції.

Приймач Лямбда.

Лямбда вираз передається як параметр. Цільове типування використовується в ряді контекстів, включаючи наступне: Змінні оголошення. Призначення операторів повернення. Инициализаторы масиву. Аргументи методу або конструктора. Лямбда-вирази. Умовні вирази. Вираз компаратор лямбда В Java.

Comparator клас використовується для сортування колекцій. У наступному прикладі виконується сортування ArrayList складається з Person об'єктів на основі surName. Нижче наведені поля, включені в Person клас. Lambda підтримує «цільову типізацію», яка вказує тип об'єкта з контексту, в якому він використовується. Оскільки присвоюється результат Comparator визначеним за допомогою generic, компілятор може зробити висновок про те, що обидва параметри мають Person тип.

Рекомендації з надання цільових типів

Метод є загальним і абстрактним, що дозволяє легко адаптуватися практично до будь-якого лямбда. Розробники повинні вивчити цей пакет перед створенням нових інтерфейсів. Скачати лямбда-вирази в Java 8 pdf можна в Інтернеті.

В деякому класі UseFoo він приймає цей інтерфейс як параметр. Якщо подивитися вираз більш уважно, то можна побачити, що Foo - це не що інше, як функція, яка приймає один аргумент і дає результат. 8-я версія вже надає такий інтерфейс функції. Можна повністю видалити інтерфейс Foo та змінити код, прописуючи вираження.

Підхід лямбда-вирази можна використовувати для будь-якого відповідного інтерфейсу із старих бібліотек. Він може використовуватися для таких інтерфейсів, як Runnable, Comparator та інших. Однак це не означає, що користувач має переглядати старшу базу коду і змінити все.

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

Однак лямбда-вирази працюють із охоплює областю і не можна перезаписувати змінні охоплює області всередині тіла лямбды. У цьому випадку до ключового слова це є посиланням на огороджує примірник.

Лямбда-вирази створюються короткими і зрозумілими, бажано використовувати одну рядкову конструкцію замість великого блоку коду. Потрібно пам'ятати, що лямбда повинна бути виразом, а не розповіддю. Незважаючи на стислий синтаксис цих виразів, вони повинні точно висловити функціональність, яку надають. Це в основному стилістичний рада, який не впливає на продуктивність.

Однак користувач не повинен використовувати це правило «однорядкової лямбды» як догми. Якщо у нього є дві або три рядки у визначенні лямбда, може виявитися недоцільним приводити їх до однієї рядку. Також необхідно уникати вказівки типів параметрів. Компілятор в більшості випадків здатний вирішувати тип лямбда-параметрів за допомогою виводу типу, тому додавання типу до параметрів є необов'язковим та може бути опущено.

Синтаксис лямбда вимагає дужок тільки навколо більше одного параметра або при відсутності якого-небудь параметра. Ось чому безпечно зробити код трохи коротше і виключити круглі дужки, коли є тільки один параметр. Такі рекомендації щодо складання лямбда-виразів Java для чайників, можна знайти в Інтернеті.

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

Лямбда-вирази чудово працюють разом тільки з функціональними інтерфейсами Java 8. Не можна використовувати лямбда-вирази з інтерфейсом з більш ніж одним абстрактним методом. Щоб працювати з подібними виразами, потрібно переконатися, що у вас встановлена восьма версія Java. Лямбда-виразу не працюють на Java 7 і більш ранніх версіях. Автор: Іван Фролов 12 Жовтня, 2018



Категория: Компьютеры