Недавно я прочитал « Состояние лямбды» Брайана Гетца, и после прочтения этой статьи я хотел попробовать использовать лямбда-выражения Java 8. В своей статье Брайан продолжает описывать интерфейсы, которые имеют один метод, как «функциональные» интерфейсы. Функциональные интерфейсы почти всегда используются в качестве анонимных классов, причем каноническим примером является ActionListener. Эти «функциональные» интерфейсы являются исходной целью для лямбда-выражений, главной целью которых является удаление большей части шаблонного или церемония вокруг их использования. Когда я писал серию статей для библиотеки Guava, интерфейс Function сразу же пришел мне на ум в качестве основного кандидата для использования лямбда-выражений.
Моя цель проста: взять модульный тест из моего блога Guava Futures (он интенсивно использует интерфейс Function) и преобразовать его в лямбда-выражения. Хотя я опишу структуру выражения lamba, поскольку оно относится к представленному примеру, этот пост не является руководством по лямбда-выражениям Java 8. Скорее это документирует мои первые попытки использования лямбда-выражений в Java.
Лямбда-выражения
Первый пример — тест для метода Futures.chain, который принимает функцию в качестве одного из аргументов:
01
02
03
04
05
06
07
08
09
10
11
12
|
Function<List<String>, ListenableFuture<List<Person>>> queryFunction = new Function<List<String>, ListenableFuture<List<Person>>>() { @Override public ListenableFuture<List<Person>> apply( final List<String> ids) { return dbService.getPersonsByIdAsync(ids); } }; ListenableFuture<List<String>> indexSearch = luceneSearcher.searchAsync( 'firstName:martin' ); ListenableFuture<List<Person>> results = Futures.chain(indexSearch,queryFunction,executorService); |
Используя выражения lamba, теперь это будет выглядеть так:
1
2
3
4
5
6
7
|
Function<List<String>, ListenableFuture<List<Person>>> queryFunction = ids ->(dbService.getPersonsByIdAsync(ids)); ListenableFuture<List<String>> indexSearch = luceneSearcher.searchAsync( 'firstName:martin' ); ListenableFuture<List<Person>>results = Futures.chain(indexSearch, queryFunction,executorService); |
Имейте в виду, что из приведенных выше примеров кода два выделенных раздела эквивалентны. Давайте посмотрим на строку 2 и объясним, как это совпадает со строками 1-7 из первого примера кода.
-
ids
— это входные данные для метода apply и соответствуетfinal List<String> ids
параметруfinal List<String> ids
в строке 4 в первом примере кода. Типы выражения lamba выводятся из контекста, в котором оно используется, поэтому нам не нужно повторять их для параметраids
. - Затем есть маркер стрелки (->), который является частью общего синтаксиса лямбда-выражений Java 8 в их текущей форме.
- Затем у нас есть тело лямбды (dbService.getPersonsByIdAsync (ids)), которое является вызовом метода, который возвращает ListenableFuture, который в свою очередь выдает объекты List of Person. Обратите внимание, что нам не нужно было помещать оператор return, так как это одно выражение, которое вычисляется и возвращается.
Следующий пример — это служебный метод из теста, который возвратил ListenableFutures, передав анонимные экземпляры Callable в ExecutorService:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
private ListenableFuture<List<Person>> getPersonsByFirstNameFuture( final String firstName, final boolean error) { return executorService.submit( new Callable<List<Person>>() { @Override public List<Person> call() throws Exception { startSignal.await(); if (error) { throw new RuntimeException( 'Ooops!' ); } List<String> ids = luceneSearcher.search( 'firstName:' + firstName); return dbService.getPersonsById(ids); } }); } |
А вот эквивалент с использованием лямбда-выражения:
1
2
3
4
5
6
7
8
9
|
private ListenableFuture<List<Person>> getPersonsByFirstNameFuture( final String firstName, final boolean error) { return executorService.submit(() -> {startSignal.await(); if (error) { throw new RuntimeException( 'Ooops!' ); } List<String> ids = luceneSearcher.search( 'firstName:' + firstName); return dbService.getPersonsById(ids); }); } |
В этом примере нет входных аргументов, поэтому выражение начинается с пустых скобок () в строке 2. Существует токен ->, но в этом примере тело содержит несколько операторов, окруженных {…}. Поскольку существует несколько операторов, в строке 7 требуется явный оператор возврата.
Среда для запуска Java 8
Мой текущий ноутбук — MacBook Pro, поэтому мне нужно было настроить среду для запуска Java 8 с поддержкой лямбды. Вот шаги, которые я предпринял:
- Установил LinuxMint 12 на VirtualBox.
- Создал каталог и поделился им с гостем LinuxMint
- Установленный разработчиком предварительный просмотр java 8.
- Чтобы получить исходный код и исходный текст теста из существующего проекта maven, я запустил
mvn jar:jar jar:test-jar
и поместил полученные файлы jar в общую директорию - Поместил все зависимости в общий каталог (guava, lucene, h2 и junit)
- Переписал модульный тест для использования лямбд и запустил новый тест из командной строки
Вывод
Хотя до выхода Java 8 с поддержкой лямбды пройдет некоторое время, то, что доступно в предварительной версии для разработчиков, выглядит многообещающе. Спасибо за ваше время и, как всегда, приветствуются комментарии и предложения.
Ресурсы
- Состояние лямбда, часть 4
- Предварительный просмотр разработчика Java 8
- Исходный код этого поста
- Гуава Проект Главная
- Исходный код для серии блогов Guava
Справка: функции Guava и Java 8 Lambdas от нашего партнера по JCG Билла Бекака в блоге Randomечет о кодировании .