[英]What's the point of Guava checkNotNull
我是Guava的新手(說實話,我不是“非常新的”,我是這個主題的新手)所以我決定閱讀一些文檔並在閱讀時非常驚訝:
com.google.common.base.Preconditions.checkNotNull(...)
我不明白這個方法的意義。 這意味着,而不是做:
myObject.getAnything();
(如果myObject為null,則可能導致NullPointerException
)
我應該用
checkNotNull(myObject).getAnything();
如果myObject
為null, 則拋出NullPointerException
如果不為null,則返回myObject
。
我很困惑,這可能是有史以來最愚蠢的問題,但......
這有什么意義? 考慮到我能想到的任何情況,這兩行與結果完全相同。
我甚至認為后者更具可讀性。
所以我一定錯過了什么。 它是什么?
想法是快速失敗。 例如,考慮這個愚蠢的類:
public class Foo {
private final String s;
public Foo(String s) {
this.s = s;
}
public int getStringLength() {
return s.length();
}
}
假設你不想允許s
空值。 (否則getStringLength
會拋出一個NPE)。 隨着課程的原樣,當你抓住那個null
,已經太晚了 - 很難找到誰把它放在那里。 罪魁禍首很可能是一個完全不同的類,並且Foo
實例可能很久以前就構建過了。 現在你必須梳理你的代碼庫,找出誰可能在那里放置一個null
值。
相反,想象一下這個構造函數:
public Foo(String s) {
this.s = checkNotNull(s);
}
現在,如果有人把一個null
在那里,你會發現馬上 -你就會有堆棧跟蹤指向正是你走錯了電話。
另一個有用的方法是,如果要在執行可以修改狀態的操作之前檢查參數。 例如,考慮這個類計算它獲得的所有字符串長度的平均值:
public class StringLengthAverager {
private int stringsSeen;
private int totalLengthSeen;
public void accept(String s) {
stringsSeen++;
totalLengthSeen += s.length();
}
public double getAverageLength() {
return ((double)totalLengthSeen) / stringsSeen;
}
}
調用accept(null)
將導致NPE被拋出 - 但是在stringsSeen
增加之前stringsSeen
。 這可能不是你想要的; 作為類的用戶,我可能期望如果它不接受空值,那么如果傳遞null,則其狀態應該保持不變(換句話說:調用應該失敗,但它不應該使對象無效)。 顯然,在這個例子中,您還可以通過在遞增stringsSeen
之前獲取s.length()
修復它,但是您可以看到如何使用更長且更復雜的方法,首先檢查所有參數是否有效可能是有用的,並且只有修改狀態:
public void accept(String s) {
checkNotNull(s); // that is, s != null is a precondition of the method
stringsSeen++;
totalLengthSeen += s.length();
}
myObject.getAnything();
(如果myObject為null,則可能導致NullPointerException)
不...只要myObject == null
它就會拋出NPE。 在Java中,沒有機會使用null
接收器調用方法(理論上的異常是靜態方法,但它們可以並且應該始終在沒有任何對象的情況下調用)。
我應該使用
checkNotNull(myObject).getAnything();
不,你不應該。 這將是相當多余的( 更新 )。
你應該使用checkNotNull
來快速失敗 。 沒有它,您可以將非法null
傳遞給另一個方法,該方法將其進一步傳遞,依此類推,等等,最終失敗。 然后你可能需要一些好運才能發現實際上第一種方法應該拒絕為null
。
yshavit的答案提到了一個重要的觀點:傳遞非法價值是不好的,但是存儲它並在以后傳遞它會更糟。
其實,
checkNotNull(myObject).getAnything()
也是有道理的,因為你明確表達了不接受任何空值的意圖。 沒有它,有人可能會認為你忘了支票並把它轉換成類似的東西
myObject != null ? myObject.getAnything() : somethingElse
OTOH,我認為檢查不值得冗長。 在一個更好的語言中 ,類型系統會考慮可空性並給我們一些語義糖
myObject!!.getAnything() // checkNotNull
myObject?.getAnything() // safe call else null
myObject?.getAnything() ?: somethingElse // safe call else somethingElse
對於可為空的myObject
,只有當myObject
被稱為非null時才允許使用標准點語法。
幾分鍾前我已經閱讀了整個帖子。 不過我很困惑為什么要使用checkNotNull
。 然后查看Guava的Precondition類doc,我得到了我的預期。 過度使用checkNotNull
肯定會降低性能。
我的想法是checkNotNull
方法值得進行數據驗證,它來自用戶直接或非常終端API到用戶交互。 它不應該用在內部API的每個方法中,因為使用它不能停止異常而是糾正內部API以避免異常。
根據DOC: Link
使用checkNotNull:
public static double sqrt(double value) {
Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
// calculate the square root
}
關於性能的警告
此類的目標是提高代碼的可讀性,但在某些情況下,這可能會帶來顯着的性能損失。 請記住,消息構造的參數值必須都是急切地計算,並且自動裝箱和varargs數組創建也可能發生,即使前置條件檢查成功(因為它幾乎總是在生產中)。 在某些情況下,這些浪費的CPU周期和分配可能會導致真正的問題。 性能敏感的前置條件檢查始終可以轉換為常規形式:
if (value < 0.0) {
throw new IllegalArgumentException("negative value: " + value);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.