歡迎光臨
每天分享高質量文章

Java 8 最佳技巧

(點選上方公眾號,可快速關註)


來源:luke,

coyee.com/article/10666-java-8-top-tips

在過去的幾年中,我一直使用Java 8 進行了很多的編碼工作,用於開發新應用和遷移遺留應用,我覺得是時候寫一些有用的”最佳實踐”。我個人不喜歡”最佳實踐”這個術語,因為它意味著“一刀切”的解決方案,當然編碼工作是不會這樣的–這是因為我們開發人員會想出適合我們的方案。但我發現我對Java8特別的喜歡,它讓我的生活更輕鬆一點,所以我想就此話題展開討論。

Optional

Optional 是一個被嚴重低估的功能, 它消除了很多困擾著我們的 NullPointerExceptions。它在程式碼邊界(包括你呼叫和提供 API)處理上特別有用,因為它允許你和你呼叫的程式碼說明程式執行的期望結果。

然而,如果沒有必要的思考和設計,那麼就會導致一個小變化而影響大量的類,也會導致可讀性變差。這裡有一些關於如何高效使用Optional的提示。

Optional 應該只用於傳回型別

…不能是引數和屬性. 閱讀這個部落格 瞭解怎樣使用 Optional。 幸運的是, IntelliJ IDEA 在開啟 inspection功能的情況下會檢查你是否遵循了這些建議。

可選值應該在使用的地方進行處理. IntelliJ IDEA的建議可以防止你不恰當的使用Optional, 所以你應該立即處理你發現的不恰當使用Optional。(根據自己的理解翻譯)

你不應該簡單的呼叫 get()

Optinal的目的是為了表示此值有可能為空,且讓你有能力來應付這種情況。因此,在使用值之前進行檢查是非常重要的。在某些情況下簡單的呼叫get()而沒有先使用isPresent()進行檢查是一樣會導致空指標問題。幸運的是,IntelliJ IDEA 任然會檢查出這個問題並警告你。

有可能是一個更優雅的方式

isPresent() 與 get()結合使用的技巧…

…但還有更優雅的解決方案。你可以使用 orElse方法來使得當它為null時給出一個代替的值。

…或者使用orElseGet方法來處理上述相同情況。這個例子和上面的看起來好像一樣,但本例是可以呼叫 supplier 介面的實現,,因此如果它是一個高開銷的方法,可以使用 lambda 運算式來獲得更好的效能。

使用Lambda運算式

Lambda 運算式是 Java 8 的賣點之一.。即使你還沒有使用過Java 8, 到目前你也可能有一些基本的瞭解。但在Java程式設計中還是一種新的方式,它也不是明顯的”最佳實踐” 。 這裡有一些我遵循的指南。

保持簡短

函式式程式員更願意使用較長的lambda 運算式,但我們這些僅僅使用Java很多年的程式員來說更容易保持lambda 運算式的短小。你甚至更喜歡把它們限制在一行,更容易把較長的運算式重構到一個方法中。

把它們變成一個方法取用, 方法取用看起來有一點陌生,但卻值得這樣做,因為在某些情況有助於提高可讀性,後面我再談可讀性。

明確的

(作者應該想要表達的是: 引數命名規範,要有意義;有更好的翻譯請修正)

lambda 運算式中型別資訊已經丟失了,因此你會發現包含型別資訊的引數會更有用。

如你所見,這樣會比較麻煩。因此我更喜歡給引數一個更有意義的命名。當然,你做與否, IntelliJ IDEA 都會讓你看到引數的型別資訊。

即使是在函式式介面的lambda 運算式中:

針對 Lambda 運算式進行設計

我認為lambda運算式有點像泛型– 泛型,我們經常使用它們 (例如, 給 List<>新增型別資訊 ),但不常見的是我們把一個方法或類泛型化 (如: Person)。同樣的, 它就像我們使用透過lambdas包裝的 Streams API,但對我們來說更罕見的是建立一個需要 lambda 運算式引數的方法。

如果你發現自己正處在這種情況的話,那麼這裡有一些不錯的技巧。

IntelliJ IDEA 可以幫助你引入一個函式化的引數

這裡讓你可以使用 Lambda 運算式而非物件來 建立一個引數 。這個功能的好處在於其建議使用一個已有的 函式介面 來匹配這個規範。

這個將引導我們

使用已有的函式介面

當開發者越來越熟悉 Java 8 程式碼時,我們會知道使用例如 Supplier 和 Consumer 這樣的介面會發生什麼,但是單獨再建立一個 ErrorMessageCreator 會讓我們很詫異並且很浪費時間。你可以翻閱 function package 來檢視系統本身已經給我們準備了什麼。

為函式介面新增 @FunctionalInterface 註解

如果你真的需要建立自己的函式介面,那麼就需要用這個 @FunctionalInterface 註解。這個註解似乎沒多大用處,但是 IntelliJ IDEA 會在介面不滿足這個註解要求的情況下予以提示。例如你沒有指定要繼承的方法:

指定太多的方法:

在類中使用註解而不是在介面:

Lambda 運算式可用於任意只包含單個抽象方法的介面中,但是不能用於滿足該要求的抽象類。看似不符合邏輯,但實際要求必須如此。

Streams

Stream API 是Java 8的另一大賣點, 我認為到現在為止,我們仍然不知道這會對我們的編碼方式有多大改變.但我發現這是一個好壞參半的功能。

流式風格

就我個人而言,更喜歡使用流式風格.當然你不必也這麼做, 但我發現它幫助了我:

  • 一眼就能看出有哪些操作,它的執行順序是什麼

  • 更方便除錯(雖然IntelliJ IDEA提供了在包含lambda運算式的行上設定斷點的能力,為了更方便除錯,把它拆分到不同的行上)

  • 在測試的時候允許取消一個操作

  • 在除錯或測試是,可以很方便的插入peek()

在我看來這樣寫很簡潔。但是使用這種方法並沒有給我們節省多少程式碼行。

你可能需要調整程式碼格式化設定讓程式碼看起來更加清晰。

使用方法取用

是的,你需要一點時間來適應這個奇怪的語法。但如果使用恰當,真的可以提升程式碼的可讀性,看看下麵程式碼:

以及使用 Objects 類的輔助方法:

後面一段程式碼更加的明確可讀。IntelliJ IDEA 通常會知道怎麼將一個 Lambda 運算式進行摺疊。

當對集合進行元素迭代時,盡可能的使用 Streams API

…或者用新的集合方法,例如 forEach. IntelliJ IDEA 會建議你這麼做:

一般來說使用 Streams API 比起迴圈和 if 陳述句組合來得更加直觀,例如:

IntelliJ IDEA 會建議這樣的寫法進行重構:

我做過的效能測試顯示這種重構帶來的結果比較奇怪,難以預測,有時候好,有時候壞,有時候沒區別。一如既往的,如果你的應用對效能問題非常在意,請認真的進行衡量。

遍歷陣列時請用 for 迴圈

然後,使用 Java 8 並不意味著你一定要使用流 API 以及集合的新方法。IntelliJ IDEA 會建議一些做法改用流的方式重構,但你不一定非得接受 (記住 inspections can be suppressed 或者 turned off).

特別是對一個原始型別的小陣列時,使用 for 迴圈的效能是最好的,而且程式碼更具可讀性(至少對 Streams API 的新手來說是這樣):

任何的技巧和提示都不是一成不變的,你應該自己決定哪裡需要使用 Streams API ,而哪裡還用迴圈操作。

看完本文有收穫?請轉發分享給更多人

關註「ImportNew」,提升Java技能

贊(0)

分享創造快樂