-
Styl funkcjonalny wprowadzony w Javie 8 pomaga nam zmniejszyć rozbieżność pomiędzy logiką biznesową a kodem. Dzięki temu możemy opowiedzieć historię w naturalnym toku, na wyższym poziomie. Zamiast mówić, jak chcesz to zrobić, możesz powiedzieć, co chcesz zrobić.
-
Kod staje się czystszy i bardziej zwięzły.
-
Funkcje wyższego rzędu pozwalają nam na:
- Wyślij funkcje do innych funkcji
- Twórz funkcje wewnątrz innych funkcji
- Zwraca funkcje z innych funkcji
To wielka wygrana dla Java, gdzie musimy w tym celu wysyłać, tworzyć i zwracać obiekty. Będziemy mogli pisać kod, który będzie bardziej niezawodny, skoncentrowany i łatwiejszy w ponownym użyciu.
-
Dzięki lambdom możemy wykonywać leniwe obliczenia. Gdy wyrażenie lambda zostanie wysłane jako argument metody, kompilator oceni je, gdy zostanie wywołane w metodzie. Różni się to od zwykłych argumentów metod, które są oceniane natychmiast.
-
Lambdy sprawiają, że pisanie testów jednostkowych staje się zabawą. Pozwalają nam tworzyć lekkie testy, które są przejrzyste, mają niewielkie rozmiary i można je szybko napisać. Możemy wykorzenić testowany kod za pomocą lambd. Dzięki temu możemy przetestować, jak różne scenariusze wpłyną na kod.
-
Nowe wzorce do nauczenia.
-
I wiele więcej!
break
, radykalnie zmieniają zachowanie pętli, zmuszając nas do zrozumienia nie tylko tego, co kod próbuje osiągnąć, continue
ale return
także zrozumienia, jak działa pętla. Teraz przyjrzymy się, jak możemy przekształcić pętle w bardziej zwięzły i czytelny kod.
Zacznijmy kodowanie!
Będziemy pracować z artykułami. Artykuł ma tytuł, autora i kilka tagów.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;
}
}
Każdy przykład będzie zawierał tradycyjne rozwiązanie wykorzystujące pętle oraz rozwiązanie wykorzystujące nowe możliwości Java 8. W pierwszym przykładzie chcemy znaleźć pierwszy artykuł w kolekcji ze znacznikiem „Java”. Przyjrzyjmy się rozwiązaniu wykorzystującemu pętlę.
public Article getFirstJavaArticle() {
for (Article article : articles) {
if (article.getTags().contains("Java")) {
return article;
}
}
return null;
}
Rozwiążmy teraz problem za pomocą operacji z Stream API.
public Optional<Article> getFirstJavaArticle() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.findFirst();
}
Całkiem fajnie, prawda? Najpierw używamy operacji filter
, aby znaleźć wszystkie artykuły ze znacznikiem „Java”, a następnie używamy, findFirst()
aby uzyskać pierwsze wystąpienie. Ponieważ strumienie są leniwe i filtr zwraca strumień, w tym podejściu elementy będą przetwarzane tylko do momentu znalezienia pierwszego dopasowania. Teraz oznaczmy wszystkie artykuły tagiem „Java”, a nie tylko pierwszy. Najpierw rozwiązanie za pomocą pętli.
public List<Article> getAllJavaArticles() {
List<Article> result = new ArrayList<>();
for (Article article : articles) {
if (article.getTags().contains("Java")) {
result.add(article);
}
}
return result;
}
Rozwiązanie wykorzystujące operacje na strumieniach.
public List<Article> getAllJavaArticles() {
return articles.stream()
.filter(article -> article.getTags().contains("Java"))
.collect(Collectors.toList());
}
W tym przykładzie użyliśmy operacji, collect
aby skrócić wynikowy strumień, zamiast deklarować kolekcję i jawnie dodawać pasujące wpisy. Jak na razie dobrze. Czas na przykłady, które sprawią, że Stream API naprawdę zabłyśnie. Pogrupujmy wszystkie artykuły według autora. Jak zwykle zaczynamy od rozwiązania problemu za pomocą pętli:
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;
}
Czy możemy znaleźć czyste rozwiązanie tego problemu za pomocą operacji na strumieniu?
public Map<String, List<Article>> groupByAuthor() {
return articles.stream()
.collect(Collectors.groupingBy(Article::getAuthor));
}
Niesamowity! Stosując operację groupingBy
i odwołanie do metody getAuthor()
otrzymujemy czysty i czytelny kod. Teraz znajdźmy resztę tagów użytych w kolekcji. Zacznijmy od przykładu pętli:
public Set<String> getDistinctTags() {
Set<String> result = new HashSet<>();
for (Article article : articles) {
result.addAll(article.getTags());
}
return result;
}
OK, przyjrzyjmy się, jak możemy rozwiązać ten problem za pomocą operacji na strumieniu:
public Set<String> getDistinctTags() {
return articles.stream()
.flatMap(article -> article.getTags().stream())
.collect(Collectors.toSet());
}
Fajny! flatmap
pomaga nam spłaszczyć listę tagów w jeden strumień wyników, którego następnie używamy collect
do utworzenia zestawu zwrotnego.
GO TO FULL VERSION