做爲搬磚黨的一族們,咱們對判空必定再熟悉不過了,不要跟我說你不多進行判空,除非你喜歡NullPointerException。java
不過NullPointerException對於不少猿們來講,也是Exception家族中最親近的一員了。android
爲了不NullPointerException來找咱們,咱們常常會進行以下操做。編程
if (data != null) {
do sth.
}
複製代碼
若是一個類中屢次使用某個對象,那你可能要一頓操做,so:設計模式
「世界第九大奇蹟」就這樣誕生了。Maybe你會想,項目中確定不止你一我的會這樣一頓操做,而後按下Command+Shift+F,真相就在眼前:api
What,咱們有接近一萬行的代碼都是在判空?安全
好了,接下來,要進入正題了。網絡
對於項目中無數次的判空,對代碼質量整潔度產生了十分之惡劣的影響,對於這種現象,咱們稱之爲「判空災難」。app
那麼,這種現象如何治理呢,你可能據說過NullObject模式,不過這不是咱們今天的武器,可是仍是須要介紹一下NullObject模式。ide
什麼是NullObject模式呢?函數
In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral ("null") behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof).
以上解析來自Wikipedia。
NullObject模式首次發表在「 程序設計模式語言 」系列叢書中。通常的,在面嚮對象語言中,對對象的調用前須要使用判空檢查,來判斷這些對象是否爲空,由於在空引用上沒法調用所需方法。
空對象模式的一種典型實現方式以下圖所示(圖片來自網絡):
示例代碼以下(命名來自網絡,哈哈究竟是有多懶):
Nullable是空對象的相關操做接口,用於肯定對象是否爲空,由於在空對象模式中,對象爲空會被包裝成一個Object,成爲Null Object,該對象會對原有對象的全部方法進行空實現。。
public interface Nullable {
boolean isNull();
}
複製代碼
這個接口定義了業務對象的行爲。
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();
}
}
複製代碼
關於空對象模式,更具體的內容你們也能夠多找一找資料,上述只是對NullObject的簡單介紹,可是,今天我要推薦的是一款協助判空的插件NR Null Object,讓咱們來優雅地進行判空,再也不進行一頓操做來定義繁瑣的空對象接口與空獨享實現類。
NR Null Object是一款適用於Android Studio、IntelliJ IDEA、PhpStorm、WebStorm、PyCharm、RubyMine、AppCode、CLion、GoLand、DataGrip等IDEA的Intellij插件。其能夠根據現有對象,便捷快速生成其空對象模式須要的組成成分,其包含功能以下:
讓咱們來看一個使用範例:
怎麼樣,看起來是否是很是快速便捷,只須要在原有須要進行屢次判空的對象所屬類中,右鍵彈出菜單,選擇Generate,並選擇NR Null Object便可自動生成相應的空對象組件。
那麼如何來得到這款插件呢?
能夠直接經過IDEA的Preferences中的Plugins倉庫進行安裝。
選擇 Preferences → Plugins → Browse repositories
搜索「NR Null Oject」或者「Null Oject」進行模糊查詢,點擊右側的Install,restart IDEA便可。
感謝評論區小夥伴們的積極補充。 關於優雅地判空,還有一種方式是使用Java8特性/Guava中的Optional來進行優雅地判空,Optional來自官方的介紹以下:
A container object which may or may not contain a non-null value. If a value is present,
isPresent()
will returntrue
andget()
will return the value.
一個可能包含也可能不包含非null值的容器對象。 若是存在值,isPresent()將返回true,get()將返回該值。
話很少說,舉個例子。
有以下代碼,須要得到Test2中的Info信息,可是參數爲Test4,咱們要一層層的申請,每一層都得到的對象均可能是空,最後的代碼看起來就像這樣。
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();
}
複製代碼
可是使用Optional後,整個就都不同了。
public String testOptional(Test test) {
return Optional.ofNullable(test).flatMap(Test::getTest3)
.flatMap(Test3::getTest2)
.map(Test2::getInfo)
.orElse("");
}
複製代碼
1.Optional.ofNullable(test),若是test爲空,則返回一個單例空Optional對象,若是非空則返回一個Optional包裝對象,Optional將test包裝;
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
複製代碼
2.flatMap(Test::getTest3)判斷test是否爲空,若是爲空,繼續返回第一步中的單例Optional對象,不然調用Test的getTest3方法;
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));
}
}
複製代碼
3.flatMap(Test3::getTest2)同上調用Test3的getTest2方法;
4.map(Test2::getInfo)同flatMap相似,可是flatMap要求Test3::getTest2返回值爲Optional類型,而map不須要,flatMap不會多層包裝,map返回會再次包裝Optional;
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));
}
}
複製代碼
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的提示。
關於Optional使用還有不少技巧,感興趣能夠查閱Guava和Java8相關書籍和文檔。
使用Optional具備以下優勢:
可是也一樣具備一些缺點:
固然,Kotlin以具備優秀的空安全性爲一大特點,並能夠與Java很好的混合使用,like this:
test1?.test2?.test3?.test4
複製代碼
若是你已經開始使用了Kotlin,能夠不用再寫繚亂的防護判空語句。若是你尚未使用Kotlin,並不推薦爲了判空優雅而直接轉向Kotlin。