Findbugs の警告:Equals メソッドは、引数の型について何も想定しないでください。
質問
私のプロジェクトで FindBugs を実行すると、上記のエラーがいくつか発生しました。
つまり、equals のオーバーライド バージョンは、RHS オブジェクトを、オーバーライド バージョンが定義されているオブジェクトと同じ型にキャストします。
ただし、私の知る限り、Java ではメソッドパラメータの変更が許可されていないため、equals パラメータに他の型を定義することはできないため、より良い設計が可能かどうかはわかりません。
私が何かとても間違ったことをしているのでしょうか、それとも FindBugs が熱心すぎるのでしょうか?
この質問を別の言い方で表現すると、次のようになります。equals に渡されたオブジェクトが LHS と同じ型ではない場合の正しい動作は何ですか:これは誤りですか、それとも例外があるべきですか?
例えば:
public boolean equals(Object rhs)
{
MyType rhsMyType = (MyType)rhs; // Should throw exception
if(this.field1().equals(rhsMyType.field1())... // Or whatever
}
解決
通常、equals を実装する場合、引数のクラスをキャストする前に、引数のクラスが実装クラスと等しい (または互換性がある) かどうかを確認できます。このようなもの:
if (getClass() != obj.getClass())
return false;
MyObj myObj = (MyObj) obj;
この方法で行うと、FindBugs 警告が表示されなくなります。
コメントに対処するための補足:
使用すべきだと主張する人もいます instanceof
の代わりに getClass
タイプセーフティをチェックします。これについては大きな議論がありますが、クラスの平等性をチェックできると述べたとき、私はそれには立ち入らないようにしていました。 または 互換性はありますが、それを避けることはできないと思います。要約すると、これを使用する場合 instanceof
クラスのインスタンスとそのサブクラスのインスタンス間の同等性をサポートできますが、対称規約を破る危険があります。 equals
. 。一般的には使用しないことをお勧めします instanceof
それが必要であることと、自分が何をしているのかを理解している場合を除きます。詳細については、以下を参照してください。
- http://www.artima.com/weblogs/viewpost.jsp?thread=4744
- JavaでequalsとhashCodeをオーバーライドする場合、どのような問題を考慮する必要がありますか?
- http://www.macchiato.com/columns/Durable5.html
- http://commons.apache.org/lang/api-release/org/apache/commons/lang/builder/EqualsBuilder.html (Apache common の実装ヘルパー)
- http://www.eclipsezone.com/eclipse/forums/t92613.rhtml (Eclipse のデフォルトのイコールジェネレータ)
- NetBeans ジェネレーターも getClass() を使用します
他のヒント
あなたは、おそらくこのような何かをやってます:
public class Foo {
// some code
public void equals(Object o) {
Foo other = (Foo) o;
// the real equals code
}
}
あなたがイコールの引数について何かを想定している。この例では():あなたは、それはFoo型のだと仮定しています。これがケースである必要はありません!あなたはまた、(あなたは、ほぼ間違いなくfalseを返す必要があり、その場合には)文字列を取得することができます。
だからあなたのコードは次のようになります:
public void equals(Object o) {
if (!(o instanceof Foo)) {
return false;
}
Foo other = (Foo) o;
// the real equals code
}
(又はデイブL言及より厳格getClass() != o.getClass()
を使用します。
また、それをこのように見えることができます:
Integer i = new Integer(42);
String s = "fourtytwo";
boolean b = i.equals(s);
このコードではなく、通常の仕上げとClassCastException
するb
を設定するfalse
を投げる必要があることを何らかの理由はありますか?
ClassCastException
への応答として.equals()
を投げることは賢明ではないでしょう。それは愚かな質問(「もちろん文字列がfooに等しいことはありません!」)であっても、それはまだ完全に罰金の答え(「ノー」== false
)で有効なものだからです。
私が言ったFindBugsの警告を無視することをお勧めしたいです。予想外のクラスのオブジェクトで呼び出されると等しい場合実際には、それはほぼ確実にバグで、あなたはバグの高速失敗します。
あなたは「ArrayListのファイル」を持っているし、(「MyFile.txtを」)files.containsを呼び出す場合は、ClassCastExceptionが得た場合、たとえば、それはいいだろう。代わりに、Javaは単にfalseを返し、あなたがそのバグを発見するまで、それはおそらく長い時間がかかります。
私はこのように私のequals(Object)を実装を開始します:
if ((object == null) || !(object instaceof ThisClass)) {
return false;
}
これはまた、FindBugsの警告を防ぎますが、ThisClassのサブクラスに渡されているときに自動的にfalse
を返しません。また、そのequals(Object)
メソッドがオーバーライドされていない場合は特に、等しいと見なされる可能性があります。