Object.requireNonNull 方法說明

Mlya 的博客

在寫代碼的時候, Android Studio 經常會提醒我們可以使用這個方法來進行參數(shù)非空檢查, 這個方法的源碼也非常簡單, 如下所示:

/**
 * Checks that the specified object reference is not {@code null}. This
 * method is designed primarily for doing parameter validation in methods
 * and constructors, as demonstrated below:
 * <blockquote><pre>
 * public Foo(Bar bar) {
 *     this.bar = Objects.requireNonNull(bar);
 * }
 * </pre></blockquote>
 *
 * @param obj the object reference to check for nullity
 * @param <T> the type of the reference
 * @return {@code obj} if not {@code null}
 * @throws NullPointerException if {@code obj} is {@code null}
 */
public static <T> T requireNonNull(T obj) {
    if (obj == null)
        throw new NullPointerException();
    return obj;
}

這個方法是 Objects 類的一個靜態(tài)方法, Objects 類是一個 Java 靜態(tài)類, 里面包含了很多 Java 工具方法, 其方法都是靜態(tài)方法, 其類的說明文檔如下:

/**
 * This class consists of {@code static} utility methods for operating
 * on objects.  These utilities include {@code null}-safe or {@code
 * null}-tolerant methods for computing the hash code of an object,
 * returning a string for an object, and comparing two objects.
 *
 * @since 1.7
 */
public final class Objects {
    ...
}

可以看出, 這個類還包括了很多關于類操作的使用工具方法, 例如比較兩個類是否相等, 計算類的 Hash Code 等方法, 這個類以后有機會進行學習和介紹.

回到 requireNonNull() 這個方法, 其源碼實現(xiàn)非常簡單, 只是進行了一個簡單的判斷, 如果所要判斷的元素為 null, 則返回空指針異常 NullPointerException, 否則直接返回對應的對象.

這看上去好像是一個多余的操作, 因為如果我們試圖去調用一個空對象的方法, 也會拋出 NullPointerException 運行時異常, 那么我們?yōu)槭裁匆啻艘慌e進行這樣的一次檢查呢? 這一問題在 StackOverflow 上有人進行了解答 Why should one use Objects.requireNonNull?.

看了他們的回答, 總結為以下幾點:

首先, 從這個方法的名稱可以看出, 這個方法使用的場景是, 我們使用一個對象的方法時, 正常的運行狀態(tài)應該能保證這個對象的引用非空, 如果這個對象為空了, 那一定是其他某個地方出錯了, 所以我們應該拋出一個異常, 我們不應該在這里處理這個非空異常.

其次, 這里涉及到一個很重要的編程思想, 就是 Fail-fast 思想, 翻譯過來就是, 讓錯誤盡可能早的出現(xiàn), 不要等到我們很多工作執(zhí)行到一半之后才拋出異常, 這樣很可能使得一部分變量處于異常狀態(tài), 出現(xiàn)更多的錯誤. 這也是 requireNonNull 這個方法的設計思想, 讓錯誤盡早出現(xiàn). 使用這個方法, 我們明確的拋出異常, 發(fā)生錯誤時, 我們立刻拋出異常.

StackOverflow 中的一個回答舉了一個具體的例子來回答這個問題, 例如有下面這樣一個類:

public class Dictionary {

    private final List<String> words;
    private final LookupService lookupService;

    public Dictionary(List<String> words) {
        this.words = this.words;
        this.lookupService = new LookupService(words);
    }

    public boolean isFirstElement(String userData) {
        return lookupService.isFirstElement(userData);
    }        
}


public class LookupService {

    List<String> words;

    public LookupService(List<String> words) {
        this.words = words;
    }

    public boolean isFirstElement(String userData) {
        return words.get(0).contains(userData);
    }
}

這里, 兩個類是包含的關系, 傳入的 List<String> 參數(shù)沒有做非空檢查. 如果我們一不小心在 Dictionary 的構造方法中傳入了 null, 如下所示:

Dictionary dictionary = new Dictionary(null); 

// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");

我們在構造時沒有任何異常, 但是當我們調用方法時, 會拋出 NPE:

Exception in thread "main" java.lang.NullPointerException
    at LookupService.isFirstElement(LookupService.java:5)
    at Dictionary.isFirstElement(Dictionary.java:15)
    at Dictionary.main(Dictionary.java:22)

JVM 告訴我們, 在執(zhí)行 return words.get(0).contains(userData) 這條語句時, 發(fā)生了異常, 但是這個異常非常不明確, 從報錯信息來看, 有多種可能會導致這個異常發(fā)生, 是因為 words 為空, 還是 words.get(0) 為空? 或者兩者都為空? 這都是不明確的. 同時, 我們也無法確定是在這兩個類的哪個環(huán)節(jié)出了錯, 這些都是不明確的, 給我們程序 debug 造成了很大的困難.

然而, 當我們使用如下方式實現(xiàn):

public Dictionary(List<String> words) {
    this.words = Objects.requireNonNull(words);
    this.lookupService = new LookupService(words);
}

按照這種實現(xiàn)方式, 在我們執(zhí)行構造方法時, 就會明確拋出錯誤.

// exception thrown early : in the constructor 
Dictionary dictionary = new Dictionary(null);

// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Exception in thread "main" java.lang.NullPointerException
    at java.util.Objects.requireNonNull(Objects.java:203)
    at com.Dictionary.(Dictionary.java:15)
    at com.Dictionary.main(Dictionary.java:24)

這樣我們進行 debug 時就明確很多, 少走很多彎路.

除此之外, 這個方法的作用也是一個明確和不明確的區(qū)別, 使用這個方法表示我們明確進行了這個判斷, 其實與我們自己使用 if-else 進行判斷是一樣的, 只是這個工具類簡化了這樣的操作, 讓我們的代碼看上去更加簡潔, 可讀性更強.

此外, requireNonNull 方法有一個重載方法, 可以提供一個報錯信息, 以供我們 debug 的時候顯示. 我們使用這個引用的時候, 應當保證非空, 如果不然, 會拋出異常告訴我們其他地方出錯了, 這里出現(xiàn)了空指針異常. 這個方法重載的實現(xiàn)如下:

/**
 * Checks that the specified object reference is not {@code null} and
 * throws a customized {@link NullPointerException} if it is. This method
 * is designed primarily for doing parameter validation in methods and
 * constructors with multiple parameters, as demonstrated below:
 * <blockquote><pre>
 * public Foo(Bar bar, Baz baz) {
 *     this.bar = Objects.requireNonNull(bar, "bar must not be null");
 *     this.baz = Objects.requireNonNull(baz, "baz must not be null");
 * }
 * </pre></blockquote>
 *
 * @param obj     the object reference to check for nullity
 * @param message detail message to be used in the event that a {@code
 *                NullPointerException} is thrown
 * @param <T> the type of the reference
 * @return {@code obj} if not {@code null}
 * @throws NullPointerException if {@code obj} is {@code null}
 */
public static <T> T requireNonNull(T obj, String message) {
    if (obj == null)
        throw new NullPointerException(message);
    return obj;
}

例如, 我們在 Android 中可以按照如下方式使用:

String username = Objects.requireNonNull(textInputLayoutUsername.getEditText(), "TextInputLayout must have an EditText as child").getText().toString();

這是一個從 TextInpuLayout 獲取用戶輸入內容的方法, 通常使用 TextInputLayout 包裹一個 EditText 來接收用戶輸入, 因此我們需要通過 TextInputLayoutgetEditText() 方法來獲取對應的 EditText, 如果我們布局有問題, 則該方法可能返回 null, 因此我們可以通過上述方法, 拋出一個明確異常, 如果運行時出現(xiàn)問題, 我們也可以很快知道是因為我們 TextInputLayout 無法獲取 EditText 而出錯的.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末骗绕,一起剝皮案震驚了整個濱河市谦炒,隨后出現(xiàn)的幾起案子购对,更是在濱河造成了極大的恐慌,老刑警劉巖侠仇,帶你破解...
    沈念sama閱讀 222,807評論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垦写,死亡現(xiàn)場離奇詭異瞎抛,居然都是意外死亡钓株,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評論 3 399
  • 文/潘曉璐 我一進店門斋荞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荞雏,“玉大人,你說我怎么就攤上這事平酿》镉牛” “怎么了?”我有些...
    開封第一講書人閱讀 169,589評論 0 363
  • 文/不壞的土叔 我叫張陵蜈彼,是天一觀的道長筑辨。 經常有香客問我,道長幸逆,這世上最難降的妖魔是什么棍辕? 我笑而不...
    開封第一講書人閱讀 60,188評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮秉颗,結果婚禮上痢毒,老公的妹妹穿的比我還像新娘送矩。我一直安慰自己蚕甥,他們只是感情好,可當我...
    茶點故事閱讀 69,185評論 6 398
  • 文/花漫 我一把揭開白布栋荸。 她就那樣靜靜地躺著菇怀,像睡著了一般凭舶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爱沟,一...
    開封第一講書人閱讀 52,785評論 1 314
  • 那天帅霜,我揣著相機與錄音,去河邊找鬼呼伸。 笑死身冀,一個胖子當著我的面吹牛,可吹牛的內容都是我干的括享。 我是一名探鬼主播搂根,決...
    沈念sama閱讀 41,220評論 3 423
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铃辖!你這毒婦竟也來了剩愧?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,167評論 0 277
  • 序言:老撾萬榮一對情侶失蹤娇斩,失蹤者是張志新(化名)和其女友劉穎仁卷,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體犬第,經...
    沈念sama閱讀 46,698評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡锦积,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,767評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了歉嗓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片充包。...
    茶點故事閱讀 40,912評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖遥椿,靈堂內的尸體忽然破棺而出基矮,到底是詐尸還是另有隱情,我是刑警寧澤冠场,帶...
    沈念sama閱讀 36,572評論 5 351
  • 正文 年R本政府宣布家浇,位于F島的核電站,受9級特大地震影響碴裙,放射性物質發(fā)生泄漏钢悲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,254評論 3 336
  • 文/蒙蒙 一舔株、第九天 我趴在偏房一處隱蔽的房頂上張望莺琳。 院中可真熱鬧,春花似錦载慈、人聲如沸惭等。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,746評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辞做。三九已至琳要,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間秤茅,已是汗流浹背稚补。 一陣腳步聲響...
    開封第一講書人閱讀 33,859評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留框喳,地道東北人课幕。 一個月前我還...
    沈念sama閱讀 49,359評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像五垮,于是被迫代替她去往敵國和親撰豺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,922評論 2 361

推薦閱讀更多精彩內容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴謹 對...
    cosWriter閱讀 11,113評論 1 32
  • 整理來自互聯(lián)網 1拼余,JDK:Java Development Kit污桦,java的開發(fā)和運行環(huán)境,java的開發(fā)工具...
    Ncompass閱讀 1,540評論 0 6
  • 寫著寫著發(fā)現(xiàn)簡書提醒我文章接近字數(shù)極限匙监,建議我換一篇寫了凡橱。 建議52:推薦使用String直接量賦值 一般對象都是...
    我沒有三顆心臟閱讀 1,351評論 2 4
  • 一、基礎知識:1亭姥、JVM稼钩、JRE和JDK的區(qū)別:JVM(Java Virtual Machine):java虛擬機...
    殺小賊閱讀 2,390評論 0 4
  • 八、深入理解java異常處理機制 引子try…catch…finally恐怕是大家再熟悉不過的語句了达罗, 你的答案是...
    壹點零閱讀 1,581評論 0 0