Java 8 中引入的函數式風格是對該語言的一個重要補充。現在Java不再是純粹的OOP,而是OOP和函數式程式設計的混合體。這是一個遊戲規則的改變者,我們需要改變我們的 OOP 大腦來吸收這些變化。 但我們為什麼要接受這些改變呢?當我們可以用純 OOP 來解決問題時,為什麼還要浪費時間去嘗試函數式風格呢?
-
Java 8中引入的函數式風格幫助我們縮小了業務邏輯和程式碼之間的差距。它使我們能夠在更高層次上以自然流暢的方式講述故事。你可以說你想做什麼,而不是說你想怎麼做。
-
程式碼變得更乾淨、更簡潔。
-
高階函數使我們能夠:
- 將函數傳送給其他函數
- 在其他函數中建立函數
- 從其他函數返回函數
這對 Java 來說是一個巨大的勝利,我們需要發送、建立和傳回物件來執行此操作。我們將能夠編寫更可靠、更有針對性、更容易重複使用的程式碼。
-
感謝 lambda,我們可以進行惰性計算。當 lambda 表達式作為方法參數發送時,編譯器將在方法中呼叫它時對其進行計算。這與立即評估的普通方法參數不同。
-
Lambda 讓編寫單元測試變得有趣。它們使我們能夠創建乾淨、尺寸小且編寫快速的輕量級測試。我們可以使用 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
幫助我們將標籤清單扁平化為單一結果流,然後我們用它collect
來建立返回集。
GO TO FULL VERSION