Статьи

Функциональное наполнение Java 8

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

Одной из наиболее интересных функций является новый API java.util.stream , который, как утверждает Javadoc, позволяет

операции в функциональном стиле над потоками элементов, такие как преобразования карты-сокращения в коллекциях

Объедините этот новый API с лямбда-выражениями, и вы получите краткий, но мощный синтаксис, который значительно упрощает код за счет применения проекций.

Взять, к примеру, якобы простую задачу фильтрации коллекции. В этом случае простая Collection типов Message , созданная так:

Создание коллекции сообщений

1
2
3
4
5
List<Message> messages = new ArrayList<>();
messages.add(new Message("aglover", "foo", 56854));
messages.add(new Message("aglover", "foo", 85));
messages.add(new Message("aglover", "bar", 9999));
messages.add(new Message("rsmith", "foo", 4564));

В этой коллекции я бы хотел отфильтровать Message с delay (3-й параметр конструктора), превышающей 3000 секунд.

До Java 8 вы могли вручную заклинить такую ​​логику следующим образом:

Фильтрация стиля старой школы

1
2
3
4
5
for (Message message : messages) {
  if (message.delay > 3000) {
    System.out.println(message);
  }
}

Однако в Java 8 эта работа становится намного более лаконичной. Коллекции теперь поддерживают stream метод, который преобразует базовую структуру данных в stream объектов, способный к итерации, и, таким образом, разрешает новое поколение функциональных операций, использующих лямбда-выражения. Большинство из этих операций также могут быть связаны. Эти цепочечные методы называются промежуточными , а методы, которые не могут быть связаны, обозначаются как терминальные .

Вкратце, лямбда-выражения во многом похожи на анонимные классы, но с гораздо меньшим синтаксисом. Например, если вы посмотрите на Javadoc для параметра метода Stream , вы увидите, что он принимает тип Predicate . Тем не менее, вам не нужно реализовывать этот интерфейс, как, скажем, до Java 8 с анонимным классом. Следовательно, лямбда-выражение Predicate для фильтрации всех значений delay больше 3000 будет:

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

1
x -> x.delay > 3000

Где x — параметр, передаваемый для каждого значения в потоке, и все, что находится справа от -> является оцененным выражением.

Объединение всего этого в Java 8 дает:

Потоковые лямбды!

1
messages.stream().filter(m -> m.delay > 3000).forEach(item -> System.out.println(item));

Интересно, что благодаря некоторым другим новым функциям Java 8, лямбда forEach может быть упрощена до:

Потоковые лямбды еще короче!

1
messages.stream().filter(m -> m.delay > 3000).forEach(System.out::println);

Поскольку параметр forEach lambda просто используется println , Java 8 теперь позволяет вам полностью удалить этот параметр.

Ранее я упоминал, что потоки позволяют вам связывать лямбды — в приведенном выше случае метод filter является промежуточным, а forEach — терминальным. Другими промежуточными методами, которые сразу распознаются функциональными программистами, являются: map , flatMap и flatMap , если назвать несколько.

Чтобы уточнить, я хотел бы найти все Message с задержкой более 3000 секунд и суммировать общее время задержки. Без функциональной магии я мог бы написать:

Прозаическая Ява

1
2
3
4
5
6
long totalWaitTime = 0;
for (Message message : messages) {
  if (message.delay > 3000) {
    totalWaitTime += message.delay;
  }
}

Тем не менее, с Java 8 и немного функционала, вы можете получить более элегантную конструкцию кода, например:

Java 8 элегантность

1
long totWaitTime = messages.stream().filter(m -> m.delay > 3000).mapToLong(m -> m.delay).sum();

Обратите внимание, как я могу mapToLong методы filter и mapToLong вместе с терминальной sum . Кстати, для метода sum требуется определенный метод стиля карты, который выдает коллекцию примитивных типов, таких как mapToLong , mapToInt и т. Д.

Функциональное программирование стиля как ключевая особенность языка — поразительно мощная конструкция. И хотя многие из этих методов были доступны в различных сторонних библиотеках, таких как языки Guava и JVM, таких как Scala и Groovy, включение этих функций в ядро ​​языка, несомненно, охватит более широкую аудиторию разработчиков и окажет наибольшее влияние на ландшафт развития.

Java 8, без сомнения, радикально меняет язык Java в лучшую сторону.

Ссылка: Функциональная разжигание Java 8 от нашего партнера JCG Эндрю Гловера в блоге The Disco Blog .