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
をスローする必要があります。
NullPointerException
がcompareTo
に適切な場合、なぜequals
に適切でないのですか?
これらは Object.equals(Object obj)
ドキュメントの実際の単語です:
一部のother objectがこのオブジェクトと「等しい」かどうかを示します。
そして、オブジェクトとは何ですか?
objectは、class instanceまたは配列です。
参照値(多くの場合、単にreferences)はこれらのオブジェクトへのポインターであり、特別な
null
参照は、が参照しますオブジェクトなし。
この角度からの私の議論は本当に簡単です。
equals
は、いくつかのotherオブジェクトが「等しい」かどうかをテストしますthis
null
リファレンスは、テストに対してother objectを提供しませんequals(null)
はNullPointerException
をスローする必要がありますこの非対称性が矛盾しているかどうかという質問には、そうではないと思います。
その瞬間、コンパイラはEnlightenmentに到達しました。
例外は、実際には例外的の状況であるべきです。 NULLポインターはプログラマーのエラーではない可能性があります。
既存の契約を引用しました。慣例に反することに決めた場合、すべてのJava開発者がequalsがfalseを返すことを期待しているとき、あなたはクラスを苦痛にさせるような予期せぬ歓迎されないことをしているでしょう。
私はこれ以上異議を唱えることはできませんでした。私は、常に例外をスローするようにイコールを書き換えません。私がそのクライアントだった場合、それを行ったクラスを置き換えます。
.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
...
}
これは必ずしもあなたの質問に対する答えではなく、その振る舞いが今のようになっていることが有用だと思うときの単なる例です。
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.
}
これは、振る舞いをそのままにするのに十分な理由ですか?知りませんが、それは有用な副作用です。
オブジェクト指向の概念を考慮し、送信者と受信者の役割全体を考慮すると、その振る舞いは便利だと思います。最初のケースでは、オブジェクトが誰にも等しくないかどうかを尋ねています。彼は「いいえ、私は違います」と言うべきです。
ただし、2番目のケースでは、誰にも参照がありません。だから、実際にはだれにも尋ねていません。これは例外をスローしますが、最初のケースはスローすべきではありません。
オブジェクトの向きを忘れて、式を数学的等式として扱う場合にのみ非対称だと思います。ただし、このパラダイムでは、両端が異なる役割を果たしているため、順序が重要であることが予想されます。
最後のポイントとして。コードにエラーがある場合は、nullポインター例外が発生します。ただし、オブジェクトに彼が誰でもないかどうかを尋ねることは、プログラミングの欠陥と見なされるべきではありません。彼がヌルでないかどうかをオブジェクトに尋ねることは完全に大丈夫だと思います。オブジェクトを提供するソースを制御しないとどうなりますか?このソースはnullを送信します。オブジェクトがnullかどうかを確認し、その後で等しいかどうかを確認しますか? 2つを比較するだけで、2番目のオブジェクトが何であれ、例外なく比較が実行される方が直感的ではないでしょうか。
正直なところ、その本体内のequalsメソッドが意図的にnullポインター例外を返すと、私は腹を立てます。 Equalsは、あらゆる種類のオブジェクトに対して使用することを意図しているため、受け取るものをそれほど気にする必要はありません。 equalsメソッドがnpeを返した場合、私の心の最後のことは、それが意図的にそれを行ったことです。特別に考慮されていない例外です。 npeを上げた場合、メソッドを呼び出す前に常にnullをチェックすることを覚えておく必要があります。さらに悪いことに、try/catchブロックでequalsへの呼び出しを囲む必要があります(神はtry/catchブロックが嫌いです) ..
個人的に、私はそれがそうであるように実行したいです。
NullPointerException
は、等しい操作が実行されているオブジェクトに問題があることを示します。
NullPointerException
が提案どおりに使用されており、(無意味な)操作を試みた場合...
o1.equals(o1)
where o1 = null ... NullPointerException
は、比較関数がねじ込まれたため、またはo1がnullであるが認識しなかったためにスローされますか?極端な例は知っていますが、現在の振る舞いでは、問題がどこにあるのか簡単にわかると思います。
最初の場合、o1.equals(o2)
はfalseを返します。これは、o1
がo2
と等しくないためです。これはまったく問題ありません。 2番目の場合、o2
はNullPointerException
であるため、null
をスローします。 null
でメソッドを呼び出すことはできません。それは一般的なプログラミング言語の制限かもしれませんが、私たちはそれに耐えなければなりません。
NullPointerException
メソッドの規約に違反しているためにequals
をスローして、必要以上に複雑なものにすることもお勧めできません。
null
が例外的ではない一般的な状況が多くあります。キーが値を持たない(例外的でない)場合を単に表すか、そうでなければ「何もない」ことを表します。したがって、未知のy
を使用してx.equals(y)
を実行することも非常に一般的であり、最初にnull
を常に確認する必要があるのは無駄な作業です。
null.equals(y)
が異なる理由については、is呼び出すプログラミングエラーany null参照のインスタンスメソッドJavaの場合 、したがって、例外に値します。 x.equals(y)
のx
とy
の順序は、x
がnull
ではないことがわかっているように選択する必要があります。ほとんどすべての場合、この並べ替えは、オブジェクトについて事前に知られていることに基づいて(たとえば、Originから、または他のメソッド呼び出しのnull
に対してチェックすることによって)実行できると主張します。
一方、両方のオブジェクトが未知の「ヌル」である場合、他のコードはほぼ確実にそれらの少なくとも1つをチェックする必要があります。または、NullPointerException
を危険にさらすことなく、オブジェクトで多くのことを実行できません。
そして、これが指定された方法であるため、契約を破り、null
のequals
引数の例外を発生させるのはプログラミングエラーです。また、例外をスローすることを要求する代替案を検討する場合、equals
のすべての実装はそれの特別なケースを作成し、equals
の呼び出しは潜在的にnull
オブジェクトは、呼び出す前に確認する必要があります。
それはcouldが異なって指定されている(つまり、equals
の前提条件は引数が非null
である必要があるため)、これはあなたの議論とは言えないは無効ですが、現在の仕様では、よりシンプルで実用的なプログラミング言語になっています。
利便性ともっと重要なのは一貫性だと思います-nullを比較の一部にすることで、null
チェックを実行し、equals
が呼び出されるたびにそのセマンティクスを実装する必要がなくなります。 null
参照は多くのコレクションタイプで有効であるため、比較の右側に表示されるのは理にかなっています。
同等性、比較などのためにインスタンスメソッドを使用すると、必然的に配置が非対称になります-多形性の大きな利益のために少し面倒です。ポリモーフィズムが必要ない場合、2つの引数MyObject.equals(MyObjecta, MyObject b)
を使用して対称静的メソッドを作成することがあります。このメソッドは、1つまたは両方の引数がnull参照かどうかを確認します。特にnull参照を除外したい場合は、追加のメソッドを作成します。 equalsStrict()
または同様のもの。他のメソッドに委任する前にnullチェックを行います。
契約は「null以外の参照x用」であることに注意してください。したがって、実装は次のようになります。
if (x != null) {
if (x.equals(null)) {
return false;
}
}
x
の次の定義が可能であるため、null
はnull
と等しいと見なされるために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;
}
これは難しい質問です。下位互換性のために、これを行うことはできません。
次のシナリオを想像してください
void m (Object o) {
if (one.equals (o)) {}
else if (two.equals (o)) {}
else {}
}
これで、equalsがfalseを返すとelse節が実行されますが、例外をスローするときは実行されません。
また、nullは実際には "2"と等しくないため、falseを返すことは完全に理にかなっています。それからおそらくfalseも返すにはnull.equals( "b")を主張する方が良いでしょう:))
しかし、この要件は奇妙で非対称な等式関係を作ります。