-
Функціональний стиль, представлений у Java 8, допомагає нам зменшити прірву між бізнес-логікою та кодом. Він дозволяє нам розповідати історію у природному потоці на вищому рівні. Замість того, щоб говорити, як ви хочете це зробити, ви можете сказати, що ви хочете зробити.
-
Код стає більш чистим та коротким.
-
Функції високого порядку дозволяють нам:
- Надсилати функції до інших функцій
- Створювати функції усередині інших функцій
- Повертати функції з інших функцій
Це велика перемога для Java, де нам потрібно відправляти, створювати і повертати об'єкти. Ми зможемо писати код, який буде більш надійним, зосередженим і легшим для повторного використання.
-
Завдяки лямбдам ми можемо робити ліниві обчислення. Коли лямбда-вираз відправляється як аргумент методу, компілятор обчислить його, коли він викликається методом. Це відрізняється від звичайних аргументів методів, які обчислюються одразу.
-
Лямбди роблять написання unit-тестів веселим. Вони дозволяють нам створювати легковажні тести, які чисті, малі за розміром та швидкі в написанні. Ми можемо корчувати код, що тестується, використовуючи лямбди. Це дозволяє нам тестувати, як усі види сценаріїв вплинуть на код.
-
Нові візерунки для вивчення.
-
І багато іншого!
break
, continue
, return
різко змінюють поведінку циклу, змушуючи нас розуміти як, чого код намагається досягти, а й розуміти, як працює цикл. Зараз ми поглянемо, як ми можемо перетворити цикли на більш короткий і читальний код.
Та почнеться кодинг!
Ми працюватимемо зі статтями. Стаття має назву, автор і кілька тегів.private class Article {
private final String title;
private final String author;
private final List<String> tags;
private Article(String title, String author, List<String> tags) {
this.title = title;
this.author = author;
this.tags = tags;
}
public String getTitle() {
return title;
}
public String getAuthor() {
return author;
}
public List<String> getTags() {
return tags;
}
}
Кожен приклад буде містити традиційне рішення з використанням циклів і рішення, що використовує нові фішки Java 8. У першому прикладі хочемо знайти в колекції першу статтю з тегом "Java". Погляньмо на рішення з використанням циклу.
public Article getFirstJavaArticle() {
for (Article article : articles) {
if (article.getTags().contains("Java")) {
return article;
}
}
return null;
}
Тепер давайте вирішимо проблему, користуючись операціями із Stream API.
public Optional<Article> getFirstJavaArticle() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.findFirst();
}
Досить таки круто, чи не так? Спочатку ми використовуємо операцію filter
для знаходження всіх статей з тегом "Java", потім використовуємо findFirst()
для отримання першого входження. Так як потоки (streams) ліниві і фільтр повертає потік, цей підхід оброблятиме елементи тільки поки не знайде перший збіг. Тепер давайте отримаємо всі статті з тегом Java, замість тільки першої. Спершу рішення за допомогою циклів.
public List<Article> getAllJavaArticles() {
List<Article> result = new ArrayList<>();
for (Article article : articles) {
if (article.getTags().contains("Java")) {
result.add(article);
}
}
return result;
}
Рішення із використанням потокових операцій.
public List<Article> getAllJavaArticles() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.collect(Collectors.toList());
}
У цьому прикладі ми використовували операцію collect
для скорочення результуючого потоку замість оголошення колекції та явного додавання статей, які підходять. Поки що все йде добре. Час для прикладів, які змусять Stream API справді блищати. Давайте згрупуємо всі статті з автора. Як завжди, починаємо з рішення за допомогою циклів:
public Map<String, List<Article>> groupByAuthor() {
Map<String, List<Article>> result = new HashMap<>();
for (Article article : articles) {
if (result.containsKey(article.getAuthor())) {
result.get(article.getAuthor()).add(article);
} else {
ArrayList<Article> articles = new ArrayList<>();
articles.add(article);
result.put(article.getAuthor(), articles);
}
}
return result;
}
Чи зможемо знайти чисте вирішення цієї проблеми, використовуючи потокові операції?
public Map<String, List<Article>> groupByAuthor() {
return articles.stream()
.collect(Collectors.groupingBy(Article::getAuthor));
}
Чудово! Використовуючи операцію groupingBy
та посилання на метод getAuthor()
, ми отримуємо чистий та читабельний код. Тепер знайдемо інші теги, що використовуються в колекції. Почнемо з циклового прикладу:
public Set<String> getDistinctTags() {
Set<String> result = new HashSet<>();
for (Article article : articles) {
result.addAll(article.getTags());
}
return result;
}
Окей, погляньмо, як ми можемо вирішити це за допомогою потокових операцій:
public Set<String> getDistinctTags() {
return articles.stream()
.flatMap(article -> article.getTags().stream())
.collect(Collectors.toSet());
}
Круто! flatmap
допомагає нам згладити список тегів в один результуючий потік, а потім ми використовуємо collect
для створення сету, що повертається.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ