web-dev-qa-db-ja.com

グアバcheckNotNullのポイントは何ですか

私はグアバはかなり新しい(正直なところ、私は「かなり新しい」ではない、私はその主題の完全な新人だ)ので、私はいくつかのドキュメントを調べて、これを読んでいる間にかなり驚いた:

com.google.common.base.Preconditions.checkNotNull(...)

この方法の要点はわかりません。これは、次のことを行う代わりに:

myObject.getAnything();

(myObjectがnullの場合、NullPointerExceptionが発生する可能性があります)

私は使うべきです

checkNotNull(myObject).getAnything();

whichwillNullPointerExceptionがnullの場合はmyObjectをスローし、nullでない場合はmyObjectを返します。

私は困惑しており、これはこれまでで最も愚かな質問かもしれませんが...

これのポイントは何ですか?これらの2行は、私が考えることができるどんな状況でも、結果とまったく同じことをします。

後者のほうが読みやすいとは思いません。

だから私は何かを見逃しているに違いない。それは何ですか?

61
Ar3s

アイデアは早く失敗することです。たとえば、この愚かなクラスを考えてみましょう。

_public class Foo {
    private final String s;

    public Foo(String s) {
        this.s = s;
    }

    public int getStringLength() {
        return s.length();
    }
}
_

sにnull値を許可したくないとしましょう。 (または、getStringLengthはNPEをスローします)。クラスをそのまま使用すると、nullをキャッチするまでには遅すぎます。誰がそこに置いているのかを見つけるのは非常に困難です。犯人はまったく別のクラスに属している可能性があり、そのFooインスタンスはかなり前に構築された可能性があります。ここで、コードベースをくまなく調べて、null値をどこに配置できるかを確認する必要があります。

代わりに、このコンストラクターを想像してください。

_public Foo(String s) {
    this.s = checkNotNull(s);
}
_

誰かがnullをそこに入れると、すぐに-がわかり、スタックトレースが、失敗した呼び出しを正確に指し示します。


もう1つの便利な方法は、状態を変更できるアクションを実行する前に引数を確認する場合です。たとえば、取得するすべての文字列の長さの平均を計算するこのクラスを検討してください。

_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がインクリメントされる前ではありません。これはあなたが望むものではないかもしれません。クラスのユーザーとして、nullを受け入れない場合、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();
    }
_
91
yshavit

myObject.getAnything();(myObjectがnullの場合、NullPointerExceptionが発生する可能性があります)

いいえ... it will _myObject == null_のたびにNPEをスローします。 Javaでは、nullレシーバーでメソッドを呼び出す可能性はありません(理論上の例外は静的メソッドですが、オブジェクトなしで常に呼び出すことができ、また呼び出す必要があります)。


checkNotNull(myObject).getAnything();を使用する必要があります

いいえ、すべきではありません。これはかなり冗長です(Update)。

fail fastを行うには、checkNotNullを使用する必要があります。それがなければ、不正なnullを別のメソッドに渡し、さらにそれをさらに渡すなどして、最終的に失敗します。その後、実際に最初のメソッドがnullを拒否する必要があることを知るために、幸運が必要になる場合があります。


Yshavitの答えは重要なポイントに言及しています:違法な値を渡すことは悪いことですが、それを保存して後で渡すことはさらに悪いことです。

更新

実際、

_ checkNotNull(myObject).getAnything()
_

nullを受け入れないという意図を明確に表明しているため、これも理にかなっています。それがなければ、誰かがあなたが小切手を忘れて、それを

_ myObject != null ? myObject.getAnything() : somethingElse
_

OTOH、私はチェックが冗長性に値するとは思わない。 より良い言語 では、型システムはnull可能性を考慮し、

_ myObject!!.getAnything()                    // checkNotNull
 myObject?.getAnything()                     // safe call else null
 myObject?.getAnything() ?: somethingElse    // safe call else somethingElse
_

ヌル可能なmyObjectの場合、標準ドット構文はmyObjectがヌルでないことがわかっている場合にのみ許可されます。

9
maaartinus

私は数分前にこのスレッド全体を読みました。それにもかかわらず、なぜcheckNotNullを使用すべきなのか混乱しました。次に、GuavaのPreconditionクラスのドキュメントを確認すると、期待した結果が得られました。 checkNotNullを過度に使用すると、パフォーマンスが確実に低下します。

私の考えは、checkNotNullメソッドはユーザーから直接来るデータ検証またはAPIからユーザーへの最後のインタラクションに必要な価値があると思います。内部APIのすべてのメソッドで使用しないでください。使用すると、例外を停止できず、内部APIを修正して例外を回避できないためです。

DOCによると:リンク

CheckNotNullの使用:

public static double sqrt(double value) {
     Preconditions.checkArgument(value >= 0.0, "negative value: %s", value);
     // calculate the square root
}

パフォーマンスに関する警告

このクラスの目標はコードの読みやすさを改善することですが、状況によってはパフォーマンスが大幅に低下する可能性があります。メッセージ構築のパラメータ値はすべて熱心に計算する必要があり、前提条件チェックが成功した場合でも、オートボクシングおよび可変引数配列の作成が発生する可能性があることに注意してください(ほとんどの場合、本番環境で行われるはずです)。状況によっては、これらの無駄なCPUサイクルと割り当てにより、実際の問題が発生する可能性があります。パフォーマンスに敏感な前提条件チェックは、常に慣習的な形式に変換できます。

if (value < 0.0) {
     throw new IllegalArgumentException("negative value: " + value);
}
4
mahbub.kuet