Javaのequals()メソッドのオーバーライド-機能しませんか?


150

equals()今日のメソッドで興味深い(そして非常にイライラする)問題に遭遇したため、十分にテストされたクラスであると考えていたクラスがクラッシュし、追跡に非常に長い時間がかかるバグが発生しました。

完全を期すために、私はIDEやデバッガーを使用していませんでした。古き良き昔ながらのテキストエディターとSystem.outを使用しています。時間は非常に限られており、学校のプロジェクトでした。

とにかく-

私は含めることができ、基本的なショッピングカート開発していたArrayListBookオブジェクトを。実現するためにはaddBook()removeBook()、およびhasBook()カートの方法を、私はかどうかを確認したかっBook既に存在していましたCart。だから私は行く-

public boolean equals(Book b) {
    ... // More code here - null checks
    if (b.getID() == this.getID()) return true;
    else return false;
}

テストではすべて正常に動作します。6つのオブジェクトを作成し、データを入力します。で多くの追加、削除、has()操作を実行するCartと、すべてが正常に動作します。私はあなたが持ってequals(TYPE var)equals(Object o) { (CAST) var }いることができるしかしそれが働いていたのでそれはあまり問題ではなかったと仮定し読んだと読んだ。

それから私はこの問題に遭遇した-私が作成するために必要Bookでオブジェクトを唯一ID Bookクラス内からインチ 他のデータは入力されません。基本的には次のとおりです。

public boolean hasBook(int i) {
    Book b = new Book(i);
    return hasBook(b);
}

public boolean hasBook(Book b) {
    // .. more code here
    return this.books.contains(b);
}

突然、このequals(Book b)方法は機能しなくなりました。これは、優れたデバッガーがなく、Cartクラスが適切にテストされ、正しいものであると想定して追跡するのに非常に長い時間がかかりました。swaappingした後equals()、次のメソッドを:

public boolean equals(Object o) {
    Book b = (Book) o;
    ... // The rest goes here   
}

すべてが再び機能し始めました。それが明らかBookオブジェクトであったとしても、メソッドがBookパラメーターを取らないことに決めた理由はありますか?唯一の違いは、それが同じクラス内からインスタンス化され、1つのデータメンバーでのみ満たされたように見えた。私は非常に混乱しています。光を当ててください


1
equalsメソッドをリフレクティブにすることで「契約」に違反したことは承知していますが、ジェネリックを使用せずにオブジェクトがArrayListに存在するかどうかをすばやく確認する方法が必要でした。
Josh Smeaton、

1
これは、Javaとequalsについて学ぶために良い教訓である
jjnguy

回答:


329

Javaでは、equals()継承されるメソッドObjectは次のとおりです。

public boolean equals(Object other);

つまり、パラメータのタイプはでなければなりませんObject。これは上書きと呼ばれます。メソッドpublic boolean equals(Book other)は、メソッドへのオーバーロードと呼ばれる処理を実行しequals()ます。

ArrayListオーバーライドされた用途equals()の方法は(そのため例えば内容を比較するcontains()equals()、メソッド)ではないオーバーロードされたもの。ほとんどのコードでは、Objectの等価を適切にオーバーライドしないものを呼び出すのは問題ありませんが、とは互換性がありませんArrayList

したがって、メソッドを正しくオーバーライドしないと、問題が発生する可能性があります。

オーバーライドするたびに次と等しい:

@Override
public boolean equals(Object other){
    if (other == null) return false;
    if (other == this) return true;
    if (!(other instanceof MyClass)) return false;
    MyClass otherMyClass = (MyClass)other;
    ...test other properties here...
}

@Override注釈を使用すると、愚かな間違いを犯したトンを助けることができます。

スーパークラスまたはインターフェイスのメソッドをオーバーライドしていると思われる場合は、常に使用してください。このように、間違った方法で実行すると、コンパイルエラーが発生します。


31
これは@Overrideアノテーションを支持する良い議論です... OPが@Overrideを使用している場合、コンパイラは親クラスのメソッドを実際にオーバーライドしていないことをコンパイラに通知します...
Cowan

1
@Overrideに気づかなかった、ありがとう。また、hashCode()のオーバーライドは実際に行われるべきであり、エラーをより早く発見した可能性があることも追加したいと思います。
Josh Smeaton、

5
一部のIDE(Eclipseなど)では、クラスメンバー変数に基づいて、equals()およびhashcode()メソッドを自動生成することもできます。
sk。

1
if (!(other instanceof MyClass))return false;他のクラスを拡張するfalse場合に戻りますMyClass。しかしfalse、他のクラスが拡張された場合は戻りませんMyClassequalそれほど矛盾してはいけませんか?
ロバート

19
instanceofを使用する場合、以前のnullcheckは冗長です。
Mateusz Dymczyk 2013

108

日食を使用する場合は、トップメニューに移動してください

ソース-> equals()およびhashCode()を生成します


同意する!これは私が以前に知らなかったものであり、それを生成することでエラーが発生しにくくなります
Boy

こっちも一緒。フレッド、ありがとう!
Anila

16
IntelliJでは、これはコード→生成…またはcontrol + Nで見つかります。:)
2014年

NetBeansであなたは...)(メニューバー>ソース(または右クリック)>コードを挿入(またはCtrl-I)に行き、とequalsの生成]をクリックします
ソロモン

11

あなたの質問に対して少し話題から外れていますが、とにかく言及する価値があります:

Commons Langには、equalsとハッシュコードのオーバーライドに使用できるいくつかの優れたメソッドがあります。EqualsBuilder.reflectionEquals(...)およびHashCodeBuilder.reflectionHashCode(...)を確認してください。過去に私に多くの頭痛の種を救いました-もちろん、IDで「等しい」ことをしたいだけなら、それはあなたの状況に合わないかもしれません。

また、@Overrideequals(またはその他のメソッド)をオーバーライドする場合は常にアノテーションを使用する必要があることに同意します。


4
あなたは日食のユーザーであれば、あなたも行くことができright click -> source -> generate hashCode() and equals()
tunaranch

1
これらのメソッドが実行時に実行されることは正しいですか?リフレクションのためにアイテムが他のアイテムと等しいかどうかをチェックするアイテムで大きなコレクションをトラバースする場合、パフォーマンスの問題が発生しませんか?
Gaket 2018年

4

ボイラープレートコードを保存するもう1つの高速なソリューションは、Lombok EqualsAndHashCodeアノテーションです。簡単で、エレガントで、カスタマイズ可能です。また、IDEには依存しません。例えば;

import lombok.EqualsAndHashCode;

@EqualsAndHashCode(of={"errorNumber","messageCode"}) // Will only use this fields to generate equals.
public class ErrorMessage{

    private long        errorNumber;
    private int         numberOfParameters;
    private Level       loggingLevel;
    private String      messageCode;

イコールで使用するフィールドをカスタマイズするには、使用可能なオプションを参照してください。ロンボクはmavenで利用可能です。提供されたスコープで追加するだけです:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.14.8</version>
    <scope>provided</scope>
</dependency>

1

Android Studioでは、alt + insert ---> equalsとhashCodeです

例:

    @Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Proveedor proveedor = (Proveedor) o;

    return getId() == proveedor.getId();

}

@Override
public int hashCode() {
    return getId();
}

1

検討してください:

Object obj = new Book();
obj.equals("hi");
// Oh noes! What happens now? Can't call it with a String that isn't a Book...

1
@エラザーどのように?objとして宣言されていObjectます。継承のポイントは、をに割り当てることができるというBookことobjです。その後ObjectStringビアがビアequals()に匹敵するべきではないことを示唆しない限り、このコードは完全に合法であり、戻りfalseます。
bcsb1001 2016年

私はまさにそれを提案します。かなり広く受け入れられていると思います。
Elazar 2016年

0

このinstanceOfステートメントは、equalsの実装でよく使用されます。

これは人気の落とし穴です!

問題は、を使用instanceOfすると対称性のルールに違反することです。

(object1.equals(object2) == true) もしそうなら (object2.equals(object1))

最初のequalsがtrueで、object2がobj1が属するクラスのサブクラスのインスタンスである場合、2番目のequalsはfalseを返します。

ob1が属すると見なされるクラスがfinalとして宣言されている場合、この問題は発生しませんが、一般に、次のようにテストする必要があります。

this.getClass() != otherObject.getClass(); そうでない場合はfalseを返し、そうでない場合はフィールドをテストして等しいかどうかを比較します。


3
メソッドのオーバーライドに関する問題を説明する大きなセクションであるBloch、Effective Java、 Item 8を参照してくださいequals()。彼はの使用を推奨していませんgetClass()。主な理由は、これを行うと、等式に影響を与えないサブクラスのリスコフ代入原理が破られるためです。
Stuart Marks

-1

recordIdはオブジェクトのプロパティです

@Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Nai_record other = (Nai_record) obj;
        if (recordId == null) {
            if (other.recordId != null)
                return false;
        } else if (!recordId.equals(other.recordId))
            return false;
        return true;
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.