簡體   English   中英

什么是番石榴checkNotNull的重點

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM