Java 8 で導入された関数型スタイルは、言語への優れた追加です。現在、Java は純粋な OOP ではなく、OOP と関数型プログラミングのハイブリッドになっています。これはゲームチェンジャーであり、これらの変化を吸収するにはOOPの脳を変える必要があります。 しかし、なぜこれらの変化を受け入れなければならないのでしょうか? 純粋な OOP を使用して問題を解決できるのに、なぜ関数型スタイルに慣れようとして時間を無駄にする必要があるのでしょうか?
-
Java 8 で導入された関数型スタイルは、ビジネス ロジックとコードの間のギャップを減らすのに役立ちます。より高いレベルで自然な流れでストーリーを伝えることができます。どうしたいかを言う代わりに、何をしたいかを言うことができます。
-
コードはよりクリーンかつ簡潔になります。
-
高次関数を使用すると、次のことが可能になります。
- 関数を他の関数に送信する
- 他の関数の中に関数を作成する
- 他の関数から関数を返す
これは Java にとって大きな利点です。Java では、これを行うためにオブジェクトを送信、作成、返す必要があります。より信頼性が高く、焦点を絞った、再利用しやすいコードを作成できるようになります。
-
ラムダのおかげで、遅延計算を行うことができます。ラムダ式がメソッドの引数として送信されると、コンパイラはメソッド内で呼び出されたときにラムダ式を評価します。これは、すぐに評価される通常のメソッド引数とは異なります。
-
Lambda を使用すると、単体テストの作成が楽しくなります。これらを使用すると、クリーンでサイズが小さく、すばやく作成できる軽量のテストを作成できます。ラムダを使用してテスト対象のコードをルートアウトできます。これにより、あらゆる種類のシナリオがコードにどのような影響を与えるかをテストできます。
-
学ぶべき新しいパターン。
-
さらにもっと!
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()
を使用して最初の記事を取得します。ストリームは遅延しており、フィルターはストリームを返すため、このアプローチでは、最初の一致が見つかるまで要素のみが処理されます。ここで、最初の記事だけではなく、「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
タグのリストを 1 つの結果ストリームに平坦化するのに役立ちます。その後、それを使用してcollect
戻りセットを作成します。
GO TO FULL VERSION