web-dev-qa-db-ja.com

equals(null)が代わりにNullPointerExceptionをスローするのは悪い考えですか?

equalsに関する null の契約は次のとおりです。

Null以外の参照値xの場合、x.equals(null)は_return false_である必要があります。

_o1 != null_および_o2 == null_の場合、次のようになります。

_o1.equals(o2) // returns false
o2.equals(o1) // throws NullPointerException
_

o2.equals(o1) throws NullPointerExceptionは、プログラマーのエラーを警告するため、良いことです。それでも、さまざまな理由でo1.equals(o2)に切り替えただけでは、そのエラーはキャッチされず、代わりに「サイレントに失敗」します。

質問は次のとおりです。

  • o1.equals(o2)NullPointerExceptionをスローする代わりに_return false_を使用するのが良い考えなのはなぜですか?
  • anyObject.equals(null)が常にNullPointerExceptionを常にスローするように、可能な限りコントラクトを書き換えるのは悪い考えでしょうか?

Comparableとの比較について

対照的に、これは Comparable contract の意味です:

nullはどのクラスのインスタンスでもないことに注意してください。e.compareTo(null)NullPointerExceptionを返しますが、e.equals(null)falseをスローする必要があります。

NullPointerExceptioncompareToに適切な場合、なぜequalsに適切でないのですか?

関連する質問


純粋に意味論的な議論

これらは Object.equals(Object obj) ドキュメントの実際の単語です:

一部のother objectがこのオブジェクトと「等しい」かどうかを示します。

そして、オブジェクトとは何ですか?

JLS 4.3.1オブジェクト

objectは、class instanceまたは配列です。

参照値(多くの場合、単にreferences)はこれらのオブジェクトへのポインターであり、特別なnull参照は、が参照しますオブジェクトなし

この角度からの私の議論は本当に簡単です。

  • equalsは、いくつかのotherオブジェクトが「等しい」かどうかをテストしますthis
  • nullリファレンスは、テストに対してother objectを提供しません
  • したがって、equals(null)NullPointerExceptionをスローする必要があります
75

この非対称性が矛盾しているかどうかという質問には、そうではないと思います。

  • 彼が次の男性と同じくらい良いかどうかを尋ねると、それぞれがイエスと言います。
  • 彼が誰とも同じくらい良いかどうか、そしてそれぞれがノーと言うかどうか、どんな人にも尋ねてください。
  • それがどんな男と同じくらい良いか誰にも尋ねないでください。あなたは決して返事をもらえません。

その瞬間、コンパイラはEnlightenmentに到達しました。

102
Sean Owen

例外は、実際には例外的の状況であるべきです。 NULLポインターはプログラマーのエラーではない可能性があります。

既存の契約を引用しました。慣例に反することに決めた場合、すべてのJava開発者がequalsがfalseを返すことを期待しているとき、あなたはクラスを苦痛にさせるような予期せぬ歓迎されないことをしているでしょう。

私はこれ以上異議を唱えることはできませんでした。私は、常に例外をスローするようにイコールを書き換えません。私がそのクライアントだった場合、それを行ったクラスを置き換えます。

19
duffymo

.equalsが==に、.compareToが比較演算子>、<、> =、<=にどのように関係するかを考えてください。

.equalsを使用してオブジェクトとnullを比較するとNPEがスローされると主張する場合、このコードもNPEをスローする必要があると言う必要があります。

Object o1 = new Object();
Object o2 = null;
boolean b = (o1 == o2); // should throw NPE here!

O1.equals(o2)とo2.equals(o1)の違いは、最初のケースでは、o1 == o2と同様に、nullと何かを比較するのに対して、2番目のケースではequalsメソッド実際に実行されることはありませんしたがって、比較はまったく行われません。

.compareToコントラクトに関して、null以外のオブジェクトとnullオブジェクトを比較することは、次のことを試みることに似ています。

int j = 0;
if(j > null) { 
   ... 
}

明らかにこれはコンパイルされません。自動アンボックス化を使用してコンパイルできますが、比較するとNPEが得られます。これは、.compareToコントラクトと一致しています。

Integer i = null;
int j = 0;
if(j > i) { // NPE
   ... 
}
8
Angus

これは必ずしもあなたの質問に対する答えではなく、その振る舞いが今のようになっていることが有用だと思うときの単なる例です。

private static final String CONSTANT_STRING = "Some value";
String text = getText();  // Whatever getText() might be, possibly returning null.

現状のままでできることです。

if (CONSTANT_STRING.equals(text)) {
    // do something.
}

そして、NullPointerExceptionが発生する可能性はありません。あなたが提案したように変更された場合、私はやらなければならないことに戻ります:

if (text != null && text.equals(CONSTANT_STRING)) {
    // do something.
}

これは、振る舞いをそのままにするのに十分な理由ですか?知りませんが、それは有用な副作用です。

4
DaveJohnston

オブジェクト指向の概念を考慮し、送信者と受信者の役割全体を考慮すると、その振る舞いは便利だと思います。最初のケースでは、オブジェクトが誰にも等しくないかどうかを尋ねています。彼は「いいえ、私は違います」と言うべきです。

ただし、2番目のケースでは、誰にも参照がありません。だから、実際にはだれにも尋ねていません。これは例外をスローしますが、最初のケースはスローすべきではありません。

オブジェクトの向きを忘れて、式を数学的等式として扱う場合にのみ非対称だと思います。ただし、このパラダイムでは、両端が異なる役割を果たしているため、順序が重要であることが予想されます。

最後のポイントとして。コードにエラーがある場合は、nullポインター例外が発生します。ただし、オブジェクトに彼が誰でもないかどうかを尋ねることは、プログラミングの欠陥と見なされるべきではありません。彼がヌルでないかどうかをオブジェクトに尋ねることは完全に大丈夫だと思います。オブジェクトを提供するソースを制御しないとどうなりますか?このソースはnullを送信します。オブジェクトがnullかどうかを確認し、その後で等しいかどうかを確認しますか? 2つを比較するだけで、2番目のオブジェクトが何であれ、例外なく比較が実行される方が直感的ではないでしょうか。

正直なところ、その本体内のequalsメソッドが意図的にnullポインター例外を返すと、私は腹を立てます。 Equalsは、あらゆる種類のオブジェクトに対して使用することを意図しているため、受け取るものをそれほど気にする必要はありません。 equalsメソッドがnpeを返した場合、私の心の最後のことは、それが意図的にそれを行ったことです。特別に考慮されていない例外です。 npeを上げた場合、メソッドを呼び出す前に常にnullをチェックすることを覚えておく必要があります。さらに悪いことに、try/catchブロックでequalsへの呼び出しを囲む必要があります(神はtry/catchブロックが嫌いです) ..

4
arg20

個人的に、私はそれがそうであるように実行したいです。

NullPointerExceptionは、等しい操作が実行されているオブジェクトに問題があることを示します。

NullPointerExceptionが提案どおりに使用されており、(無意味な)操作を試みた場合...

o1.equals(o1) where o1 = null ... NullPointerExceptionは、比較関数がねじ込まれたため、またはo1がnullであるが認識しなかったためにスローされますか?極端な例は知っていますが、現在の振る舞いでは、問題がどこにあるのか簡単にわかると思います。

2
Rob L

最初の場合、o1.equals(o2)はfalseを返します。これは、o1o2と等しくないためです。これはまったく問題ありません。 2番目の場合、o2NullPointerExceptionであるため、nullをスローします。 nullでメソッドを呼び出すことはできません。それは一般的なプログラミング言語の制限かもしれませんが、私たちはそれに耐えなければなりません。

NullPointerExceptionメソッドの規約に違反しているためにequalsをスローして、必要以上に複雑なものにすることもお勧めできません。

2
fastcodejava

nullが例外的ではない一般的な状況が多くあります。キーが値を持たない(例外的でない)場合を単に表すか、そうでなければ「何もない」ことを表します。したがって、未知のyを使用してx.equals(y)を実行することも非常に一般的であり、最初にnullを常に確認する必要があるのは無駄な作業です。

null.equals(y)が異なる理由については、is呼び出すプログラミングエラーany null参照のインスタンスメソッドJavaの場合 、したがって、例外に値します。 x.equals(y)xyの順序は、xnullではないことがわかっているように選択する必要があります。ほとんどすべての場合、この並べ替えは、オブジェクトについて事前に知られていることに基づいて(たとえば、Originから、または他のメソッド呼び出しのnullに対してチェックすることによって)実行できると主張します。

一方、両方のオブジェクトが未知の「ヌル」である場合、他のコードはほぼ確実にそれらの少なくとも1つをチェックする必要があります。または、NullPointerExceptionを危険にさらすことなく、オブジェクトで多くのことを実行できません。

そして、これが指定された方法であるため、契約を破り、nullequals引数の例外を発生させるのはプログラミングエラーです。また、例外をスローすることを要求する代替案を検討する場合、equalsのすべての実装はそれの特別なケースを作成し、equalsの呼び出しは潜在的にnullオブジェクトは、呼び出す前に確認する必要があります。

それはcouldが異なって指定されている(つまり、equalsの前提条件は引数が非nullである必要があるため)、これはあなたの議論とは言えないは無効ですが、現在の仕様では、よりシンプルで実用的なプログラミング言語になっています。

2
Arkku

利便性ともっと重要なのは一貫性だと思います-nullを比較の一部にすることで、nullチェックを実行し、equalsが呼び出されるたびにそのセマンティクスを実装する必要がなくなります。 null参照は多くのコレクションタイプで有効であるため、比較の右側に表示されるのは理にかなっています。

同等性、比較などのためにインスタンスメソッドを使用すると、必然的に配置が非対称になります-多形性の大きな利益のために少し面倒です。ポリモーフィズムが必要ない場合、2つの引数MyObject.equals(MyObjecta, MyObject b)を使用して対称静的メソッドを作成することがあります。このメソッドは、1つまたは両方の引数がnull参照かどうかを確認します。特にnull参照を除外したい場合は、追加のメソッドを作成します。 equalsStrict()または同様のもの。他のメソッドに委任する前にnullチェックを行います。

1
mdma

契約は「null以外の参照x用」であることに注意してください。したがって、実装は次のようになります。

if (x != null) {
    if (x.equals(null)) {
        return false;
    }
}

xの次の定義が可能であるため、nullnullと等しいと見なされるためにequalsである必要はありません。

public boolean equals(Object obj) {
    // ...
    // If someMember is 0 this object is considered as equal to null.
    if (this.someMember == 0 and obj == null) {
         return true;
    }
    return false;
}
1
Vijay Mathew

これは難しい質問です。下位互換性のために、これを行うことはできません。

次のシナリオを想像してください

void m (Object o) {
 if (one.equals (o)) {}
 else if (two.equals (o)) {}
 else {}
}

これで、equalsがfalseを返すとelse節が実行されますが、例外をスローするときは実行されません。

また、nullは実際には "2"と等しくないため、falseを返すことは完全に理にかなっています。それからおそらくfalseも返すにはnull.equals( "b")を主張する方が良いでしょう:))

しかし、この要件は奇妙で非対称な等式関係を作ります。

0
Anton