Javaでequalsメソッドをオーバーライドする方法


108

Javaでequalsメソッドをオーバーライドしようとしています。私はPeople基本的に2つのデータフィールドnameとを持つクラスを持っていますage。次に、equalsメソッドをオーバーライドして、2つのPeopleオブジェクト間を確認できるようにします。

私のコードは次のとおりです

public boolean equals(People other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(other.name) &&  age.equals(other.age);
    } // end else

    return result;
} // end equals

しかし、私が書くときage.equals(other.age)、equalsメソッドはStringのみを比較でき、年齢はIntegerであるため、エラーが発生します。

解決

==提案されたように演算子を使用して、私の問題は解決しました。


3
this.age == other.ageはどうですか?:)
denis.solonenko

1
年齢のデータ型は何ですか?int OR Integer?また、使用しているJDKのバージョンは何ですか?
マニッシュ、2011年

2
「equalsメソッドは文字列のみを比較できるため」-equalsメソッドは文字列のみを比較できると誰が言ったか equalsメソッドはObjectクラスに属し、作成されたクラスはデフォルトでequals実装を持ちます。すべてのJavaクラスでイコールを呼び出すことができます
マニッシュ

回答:


127
//Written by K@stackoverflow
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ArrayList<Person> people = new ArrayList<Person>();
        people.add(new Person("Subash Adhikari", 28));
        people.add(new Person("K", 28));
        people.add(new Person("StackOverflow", 4));
        people.add(new Person("Subash Adhikari", 28));

        for (int i = 0; i < people.size() - 1; i++) {
            for (int y = i + 1; y <= people.size() - 1; y++) {
                boolean check = people.get(i).equals(people.get(y));

                System.out.println("-- " + people.get(i).getName() + " - VS - " + people.get(y).getName());
                System.out.println(check);
            }
        }
    }
}

//written by K@stackoverflow
    public class Person {
        private String name;
        private int age;

        public Person(String name, int age){
            this.name = name;
            this.age = age;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }

            
if (obj.getClass() != this.getClass()) {
                return false;
            }



            final Person other = (Person) obj;
            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
                return false;
            }

            if (this.age != other.age) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            int hash = 3;
            hash = 53 * hash + (this.name != null ? this.name.hashCode() : 0);
            hash = 53 * hash + this.age;
            return hash;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

出力:

実行:

-Subash Adhikari-VS-K false

-Subash Adhikari-VS-StackOverflow false

-Subash Adhikari-VS-Subash Adhikari true

-K-VS-StackOverflow false

-K-VS-Subash Adhikari false

-StackOverflow-VS-Subash Adhikari false

-成功したビルド(合計時間:0秒)


7
hash = 53 * hashそれを使用している理由は何ですか?
kittu 2015

2
getClass()クラスをサブクラス化し、スーパークラスのオブジェクトと比較すると、使用すると問題が発生します。
Tuxdude、2015

1
bcozかもしれ53はある素数は、この回答を見ていstackoverflow.com/a/27609/3425489を、彼がで数字を選択しながら、コメントhashCode()
シャンタラムTupe

1
この質問に勝利の答えは、あなたがのhashCode()をオーバーライドする理由の優れた解説があるstackoverflow.com/a/27609/1992108
Pegasaurus

7
instanceof演算子or を使用するのではなく、if(getClass()!= obj.getClass())...を使用することを検討してくださいisAssignableFrom。これには、サブタイプの一致ではなく、完全なタイプの一致が必要です。-対称要件。Stringまたは他のオブジェクトタイプを比較するために、を使用できますObjects.equals(this.name,other.name)
YoYo

22

パラメータタイプを変更する新しいメソッドシグネチャの導入は、オーバーロードと呼ばれます。

public boolean equals(People other){

これPeopleはとは異なりObjectます。

メソッドのシグネチャがスーパークラスのシグネチャと同一のままである場合、それはオーバーライドと呼ばれ、@Override注釈はコンパイル時に2つを区別するのに役立ちます。

@Override
public boolean equals(Object other){

の実際の宣言を見なければage、エラーが発生する理由を判断することは困難です。


18

コード全体を投稿していないため、詳細はわかりませんが、

  • hashCode()同様にオーバーライドすることを忘れないでください
  • equals方法は持つべきObjectではない、Peopleその引数の型として。現時点では、equalsメソッドをオーバーライドするのではなく、オーバーロードしています。これは、特に後で型を確認する場合、おそらく望んでいる方法ではありません。
  • あなたはinstanceofそれがPeopleオブジェクトであることを確認するために使用できます例えばif (!(other instanceof People)) { result = false;}
  • equalsすべてのオブジェクトに使用されますが、プリミティブには使用されません。私はあなたが年齢がint(原始的)であることを意味していると思います、その場合は単に使用してください==。整数(大文字の 'I'付き)は、等しいと比較する必要のあるオブジェクトであることに注意してください。

JavaでequalsとhashCodeをオーバーライドするときに考慮すべき問題を参照してください詳細については。


12
@Override
public boolean equals(Object that){
  if(this == that) return true;//if both of them points the same address in memory

  if(!(that instanceof People)) return false; // if "that" is not a People or a childclass

  People thatPeople = (People)that; // than we can cast it to People safely

  return this.name.equals(thatPeople.name) && this.age == thatPeople.age;// if they have the same name and same age, then the 2 objects are equal unless they're pointing to different memory adresses
}

12

項目10:イコールをオーバーライドするときは、一般契約に従います

Effective Javaよるとequalsメソッドのオーバーライドは単純に見えますが、それを誤解させる方法はたくさんあり、結果は悲惨なものになる可能性があります。問題を回避する最も簡単な方法は、equalsメソッドをオーバーライドしないことです。その場合、クラスの各インスタンスはそれ自体とのみ等しくなります。これは、次の条件のいずれかが当てはまる場合に行うべき正しいことです。

  • クラスの各インスタンスは本質的に一意です。これは、値ではなくアクティブなエンティティを表すThreadなどのクラスに当てはまります。Objectによって提供されるequals実装は、これらのクラスに対して正確に正しい動作をします。

  • クラスが「論理的同等性」テストを提供する必要はありません。たとえば、java.util.regex.Patternはequalsをオーバーライドして、2つのPatternインスタンスがまったく同じ正規表現を表すかどうかを確認できますが、設計者はクライアントがこの機能を必要とする、または必要とするとは考えていませんでした。このような状況では、Objectから継承したequals実装が理想的です。

  • スーパークラスはすでにequalsをオーバーライドしており、スーパークラスの動作はこのクラスに適しています。たとえば、ほとんどのSet実装は、AbstractSetからのequals実装、AbstractListからのList実装、およびAbstractMapからのMap実装を継承します。

  • クラスがprivateまたはpackage-privateであり、そのequalsメソッドが呼び出されないことは確かです。非常にリスクを嫌う場合は、equalsメソッドをオーバーライドして、誤って呼び出されないようにすることができます。

このequalsメソッドは同値関係を実装します。次のプロパティがあります。

  • 再帰:null以外の参照値の場合xx.equals(x)trueを返す必要があります。

  • 対称:null以外の基準値についてxとはyx.equals(y)とy.equals(x)がtrueを返す場合にのみ場合にtrueを返す必要があります。

  • 推移は:null以外の参照値についてxyz、あればx.equals(y)リターンtruey.equals(z)戻りtrue、その後、x.equals(z)返さなければなりませんtrue

  • 一貫性:null以外の参照値xとのy場合、等しい比較で使用される情報が変更されていない限り、の複数の呼び出しでx.equals(y)一貫してを返すtrueか、一貫してを返す必要がありfalseます。

  • null以外の参照値の場合はxx.equals(null)を返す必要がありfalseます。

高品質のequalsメソッドのレシピは次のとおりです。

  1. ==演算子を使用して、引数がこのオブジェクトへの参照であるかどうかを確認します。その場合は、trueを返します。これは単なるパフォーマンスの最適化ですが、比較に潜在的にコストがかかる場合に実行する価値があります。

  2. instanceof演算子を使用して、引数の型が正しいかどうかを確認します。そうでない場合は、falseを返します。通常、正しいタイプは、メソッドが発生するクラスです。時々、それはこのクラスによって実装されるいくつかのインターフェースです。クラスが、equalsコントラクトを改良してインターフェイスを実装するクラス間の比較を可能にするインターフェイスを実装する場合は、インターフェイスを使用します。Set、List、Map、Map.Entryなどのコレクションインターフェイスには、このプロパティがあります。

  3. 引数を正しい型にキャストしてください。このキャストの前にinstanceofテストが実行されたため、成功することが保証されています。

  4. クラスの「重要な」フィールドごとに、引数のそのフィールドがこのオブジェクトの対応するフィールドと一致するかどうかを確認します。これらすべてのテストが成功した場合は、trueを返します。それ以外の場合は、falseを返します。手順2の型がインターフェイスの場合、インターフェイスメソッドを介して引数のフィールドにアクセスする必要があります。タイプがクラスの場合、そのアクセシビリティによっては、フィールドに直接アクセスできる場合があります。

  5. タイプがまたはでないプリミティブフィールドのfloat場合double==比較には演算子を使用します。オブジェクト参照フィールドの場合、equalsメソッドを再帰的に呼び出します。用float分野、静的な使用Float.compare(float, float)方法を。そして、のためにdoubleフィールドを使用しますDouble.compare(double, double)。floatおよびdoubleフィールドの特別な処理は、の存在と、類似のdouble値によって必要Float.NaN-0.0fなります。あなたが比較可能であるがfloatdoubleフィールド静的メソッドでFloat.equalsDouble.equals、これはパフォーマンスの低下を持っているでしょう、すべての比較、上のオートボクシングを伴うだろう。以下のためにarrayフィールド、各要素にこれらのガイドラインを適用します。配列フィールドのすべての要素が重要な場合は、いずれかのArrays.equals方法を使用します。

  6. 一部のオブジェクト参照フィールドには、が正当に含まれる場合がありますnull。の可能性を回避するにはNullPointerException、静的メソッドを使用して、そのようなフィールドが等しいかどうかを確認しますObjects.equals(Object, Object)

    // Class with a typical equals method
    
    public final class PhoneNumber {
    
        private final short areaCode, prefix, lineNum;
    
        public PhoneNumber(int areaCode, int prefix, int lineNum) {
    
            this.areaCode = rangeCheck(areaCode,  999, "area code");
    
            this.prefix   = rangeCheck(prefix,    999, "prefix");
    
            this.lineNum  = rangeCheck(lineNum,  9999, "line num");
    
        }
    
        private static short rangeCheck(int val, int max, String arg) {
    
            if (val < 0 || val > max)
    
               throw new IllegalArgumentException(arg + ": " + val);
    
            return (short) val;
    
        }
    
        @Override public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof PhoneNumber))
                return false;
            PhoneNumber pn = (PhoneNumber)o;
            return pn.lineNum == lineNum && pn.prefix == prefix
                    && pn.areaCode == areaCode;
        }
        ... // Remainder omitted
    
    }
    

1
hashCode()同様にオーバーライドする必要があることを忘れないでください。また、Java7書き込みいるのでそのノート、equals()およびhashCode()方法使用してはるかに簡単になってきているObjects.equals()Arrays.equals()Objects.hashCode()Arrays.hashCode()
Arnold Schrijver

3
if (getClass() != obj.getClass()) ...instanceof演算子を使用するのではなく、使用することを検討してください。これには、サブタイプの一致ではなく、完全なタイプの一致が必要です。-対称要件。
YoYo

@YoYoは正しいです... instanceofを使用すると、対称プロパティが失敗する可能性があります。oがPhoneNumberWithExtensionなどのPhoneNumberのサブクラスであり、それがinstanceofを使用して同じようにオーバーライドされる場合、o.equals(this)はinstanceofテストに失敗し、PhoneNumber.equalsはそれを渡し、trueを返します(他のすべてのPhoneNumberフィールドを想定)等しいです)。
ldkronos

5

私はageタイプであると推測しているのでint

public boolean equals(Object other){
    boolean result;
    if((other == null) || (getClass() != other.getClass())){
        result = false;
    } // end if
    else{
        People otherPeople = (People)other;
        result = name.equals(otherPeople.name) &&  age == otherPeople.age;
    } // end else

    return result;
} // end equals

これはNullPointerExceptionif nameisになりnullます。
オリエン

@orien大したことではない、多分それは値をname割り当てられない契約にあるnull...
fortran

@fortranだから...多分それは大したことではない;)
orien

5

Javaでオブジェクトを比較するときは、セマンティックチェックを行い、オブジェクトのタイプを比較して状態識別します。

  • 自体(同じインスタンス)
  • 自体(クローン、または再構築されたコピー)
  • 異なるタイプの他のオブジェクト
  • 同じタイプの他のオブジェクト
  • null

ルール:

  • 対称性a.equals(b) == b.equals(a)
  • equals()常に利回りtrueまたはfalse、決してNullpointerExceptionClassCastExceptionまたは任意の他のスロー可能

比較:

  • 型チェック:両方のインスタンスは同じ型である必要があります。つまり、実際のクラスが等しいかどうかを比較する必要があります。開発者instanceofが型の比較に使用する場合、これは正しく実装されないことがよくあります(サブクラスがない場合にのみ機能し、の場合は対称規則に違反しA extends B -> a instanceof b != b instanceof a)ます)。
  • 状態を識別するセマンティックチェック:インスタンスが識別される状態を理解するようにしてください。人は、社会保障番号で識別できますが、髪の色(染色可能)、名前(変更可能)、または年齢(常に変更)では識別できません。値オブジェクトの場合のみ、完全な状態(すべての非一時フィールド)を比較する必要があります。それ以外の場合は、インスタンスを識別するものだけを確認してください。

あなたのPersonクラス:

public boolean equals(Object obj) {

    // same instance
    if (obj == this) {
        return true;
    }
    // null
    if (obj == null) {
        return false;
    }
    // type
    if (!getClass().equals(obj.getClass())) {
        return false;
    }
    // cast and compare state
    Person other = (Person) obj;
    return Objects.equals(name, other.name) && Objects.equals(age, other.age);
}

再利用可能な汎用ユーティリティクラス:

public final class Equals {

    private Equals() {
        // private constructor, no instances allowed
    }

    /**
     * Convenience equals implementation, does the object equality, null and type checking, and comparison of the identifying state
     *
     * @param instance       object instance (where the equals() is implemented)
     * @param other          other instance to compare to
     * @param stateAccessors stateAccessors for state to compare, optional
     * @param <T>            instance type
     * @return true when equals, false otherwise
     */
    public static <T> boolean as(T instance, Object other, Function<? super T, Object>... stateAccessors) {
        if (instance == null) {
            return other == null;
        }
        if (instance == other) {
            return true;
        }
        if (other == null) {
            return false;
        }
        if (!instance.getClass().equals(other.getClass())) {
            return false;
        }
        if (stateAccessors == null) {
            return true;
        }
        return Stream.of(stateAccessors).allMatch(s -> Objects.equals(s.apply(instance), s.apply((T) other)));
    }
}

あなたのためにPersonクラス、このユーティリティクラスを使用しました:

public boolean equals(Object obj) {
    return Equals.as(this, obj, t -> t.name, t -> t.age);
}

1

ageがintの場合、==を使用する必要があります。Integerオブジェクトの場合、equals()を使用できます。equalsをオーバーライドする場合は、ハッシュコードメソッドも実装する必要があります。契約の詳細は、ObjectのjavadocとWebのさまざまなページで入手できます。


0

私が最近使用したソリューションは次のとおりです。

public class Test {
    public String a;
    public long b;
    public Date c;
    public String d;
    
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Test)) {
            return false;
        }
        Test testOther = (Test) obj;
        return (a != null ? a.equals(testOther.a) : testOther.a == null)
                && (b == testOther.b)
                && (c != null ? c.equals(testOther.c) : testOther.c == null)
                && (d != null ? d.equals(testOther.d) : testOther.d == null);
    }

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.