(點選上方公眾號,可快速關註)
來源: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
如果你發現自己正處在這種情況的話,那麼這裡有一些不錯的技巧。
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技能