對於專案中無數次的判空,對程式碼品質整齊度產生了十分之惡劣的影響,對於這種現象,我們稱之為「判空災難」。
那麼,這種現像如何治理呢,你可能聽過NullObject模式,不過這不是我們今天的武器,但還是需要介紹一下NullObject模式。
In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral ("null") an object with no referenced value or with defined neutral ("null") behavior. The null object behavior (or lack thereof).
以上解析來自Wikipedia。
NullObject模式首次發表在「程式設計模式語言 」系列叢書中。一般的,在物件導向語言中,物件的呼叫前需要使用判空檢查,來判斷這些物件是否為空,因為在空引用上無法呼叫所需方法。
範例程式碼如下(命名來自網絡,哈哈到底是有多懶):
Nullable是空物件的相關操作接口,用於確定物件是否為空,因為在空物件模式中,物件為空會被包裝成一個Object,成為Null Object,該物件會對原有物件的所有方法進行空實作…
public interface Nullable { boolean isNull(); }
這個介面定義了業務物件的行為。
<br>
public interface DependencyBase extends Nullable { void Operation(); }
這是該物件的真實類,實作了業務行為介面DependencyBase與空物件操作介面Nullable。
public class Dependency implements DependencyBase, Nullable { @Override public void Operation() { System.out.print("Test!"); } @Override public boolean isNull() { return false; } }
這是空對象,對原有對象的行為進行了空實現。
public class NullObject implements DependencyBase{ @Override public void Operation() { // do nothing } @Override public boolean isNull() { return true; } }
在使用時,可以透過工廠調用方式來進行空物件的調用,也可以透過其他如反射的方式對物件進行調用(一般多耗時幾毫秒)在此不進行詳細敘述。
public class Factory { public static DependencyBase get(Nullable dependencyBase){ if (dependencyBase == null){ return new NullObject(); } return new Dependency(); } }
這是一個使用範例,透過這個模式,我們不再需要進行物件的判空操作,而是可以直接使用對象,也不必擔心NPE(NullPointerException)的問題。
public class Client { public void test(DependencyBase dependencyBase){ Factory.get(dependencyBase).Operation(); } }
NR Null Object是一款適用於Android Studio、IntelliJ IDEA、PhpStorm、WebStorm、PyCharm、RubyMine、AppCode、CLion、GoLand、DataGrip等IDEA的Intellij插件。其可依據現有對象,便捷快速產生其空對像模式所需的組成成分,其包含功能如下:
分析所選類別可宣告為介面的方法;
抽像出公有介面;
建立空對象,自動實作公有介面;
對部分函數進行可為空宣告;
可追加函數進行再次產生;
自動的函數命名規範
怎麼樣,看起來是不是非常快速便捷,只需要在原有需要進行多次判空的對象中,郵件彈出選單,選擇Generate,並選擇NR Null Object即可自動產生對應的空物件元件。
可以直接透過IDEA的Preferences中的Plugins倉庫來安裝。
##搜尋「NR Null Oject」或「Null Oject」進行模糊查詢,點選右側的Install,restart IDEA即可。 Optional還有一種方式是使用Java8特性中的Optional來進行優雅地判空,Optional來自官方的介紹如下:選擇Preferences → Plugins → Browse repositories
A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the #value.#value.
# #一個可能包含也可能不包含非null值的容器物件。如果存在值,isPresent()將傳回true,get()將傳回該值。
話不多說,舉個例子。
有以下程式碼,需要取得Test2中的Info信息,但是參數為Test4,我們要一層層的申請,每一層都獲得的物件都可能是空,最後的程式碼看起來就像這樣。
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">public String testSimple(Test4 test) { if (test == null) { return ""; } if (test.getTest3() == null) { return ""; } if (test.getTest3().getTest2() == null) { return ""; } if (test.getTest3().getTest2().getInfo() == null) { return ""; } return test.getTest3().getTest2().getInfo(); }</pre><div class="contentsignin">登入後複製</div></div>
但是使用Optional後,整個就都不一樣了。
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">public String testOptional(Test test) { return Optional.ofNullable(test).flatMap(Test::getTest3) .flatMap(Test3::getTest2) .map(Test2::getInfo) .orElse(""); }</pre><div class="contentsignin">登入後複製</div></div>
1、Optional.ofNullable(test),如果test為空,則傳回一個單例空Optional對象,如果非空則傳回一個Optional包裝對象,Optional將test包裝;
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">public static <t> Optional<t> ofNullable(T value) { return value == null ? empty() : of(value); }</t></t></pre><div class="contentsignin">登入後複製</div></div>
2、flatMap(Test::getTest3)判斷test是否為空,如果為空,繼續返回第一步中的單例Optional對象,否則呼叫Test的getTest3方法;
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">public<u> Optional<u> flatMap(Function super T, Optional<u>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } }</u></u></u></pre><div class="contentsignin">登入後複製</div></div>
<p>3、flatMap(Test3::getTest2)同上调用Test3的getTest2方法;</p>
<p>4、map(Test2::getInfo)同flatMap类似,但是flatMap要求Test3::getTest2返回值为Optional类型,而map不需要,flatMap不会多层包装,map返回会再次包装Optional;<code>
public<u> Optional<u> map(Function super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }</u></u>
5、orElse("");获得map中的value,不为空则直接返回value,为空则返回传入的参数作为默认值。
public T orElse(T other) { return value != null ? value : other; }
怎么样,使用Optional后我们的代码是不是瞬间变得非常整洁,或许看到这段代码你会有很多疑问,针对复杂的一长串判空,Optional有它的优势,但是对于简单的判空使用Optional也会增加代码的阅读成本、编码量以及团队新成员的学习成本。毕竟Optional在现在还并没有像RxJava那样流行,它还拥有一定的局限性。
如果直接使用Java8中的Optional,需要保证安卓API级别在24及以上。
你也可以直接引入Google的Guava。(啥是Guava?来自官方的提示)
Guava is a set of core libraries that includes new collection types (such as multimap and multiset), immutable collections, a graph library, functional types, an in-memory cache, and APIs/utilities for concurrency, I/O, hashing, primitives, reflection, string processing, and much more!
引用方式,就像这样:
dependencies { compile 'com.google.guava:guava:27.0-jre' // or, for Android: api 'com.google.guava:guava:27.0-android' }
不过IDEA默认会显示黄色,提示让你将Guava表达式迁移到Java Api上。
当然,你也可以通过在Preferences搜索"Guava"来Kill掉这个Yellow的提示。
将防御式编程代码完美包装
链式调用
有效避免程序代码中的空指针
流行性不是非常理想,团队新成员需要学习成本
安卓中需要引入Guava,需要团队每个人处理IDEA默认提示,或者忍受黄色提示
当然,Kotlin以具有优秀的空安全性为一大特色,并可以与Java很好的混合使用,like this:
test1?.test2?.test3?.test4
以上是Java判空如何實現的詳細內容。更多資訊請關注PHP中文網其他相關文章!