web-dev-qa-db-ja.com

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

Javaでequalsメソッドをオーバーライドしようとしています。基本的に2つのデータフィールドPeopleおよび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のみを比較でき、ageはIntegerであるため、エラーが発生します。

溶液

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

94
user702026
//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 (!Person.class.isAssignableFrom(obj.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秒)

113
Kim

まず第一に、あなたはオーバーライドequalsではなく、オーバーロードそれです。

ageの実際の宣言がなければ、なぜエラーが発生しているのかを説明するのは困難です。

20
fortran

コード全体を投稿していないので詳細はわかりませんが、次のとおりです。

  • hashCode()もオーバーライドすることを忘れないでください
  • equalsメソッドには、引数タイプとしてObjectではなくPeopleが必要です。現時点では、equalsメソッドをオーバーライドするのではなく、オーバーロードしています。これは、おそらく後でタイプを確認することを考えると、おそらく望んでいないものです。
  • instanceofを使用して、それがPeopleオブジェクトであることを確認できます。 if (!(other instanceof People)) { result = false;}
  • equalsはすべてのオブジェクトに使用されますが、プリミティブには使用されません。年齢はint(プリミティブ)であり、その場合は==を使用するだけだと思います。整数(大文字の「I」)は、等しいと比較する必要があるオブジェクトであることに注意してください。

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

17
Adrian Mouat
@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
}
10
NeverJr

項目10:等しい場合にオーバーライドする場合は、一般契約に従います

Effective Javaによるequalsメソッドのオーバーライドは簡単に思えますが、それを間違える多くの方法があり、結果は悲惨な場合があります。問題を回避する最も簡単な方法は、equalsメソッドをオーバーライドしないことです。この場合、クラスの各インスタンスはそれ自体にのみ等しくなります。これは、次の条件のいずれかが当てはまる場合に正しいことです。

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

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

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

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

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

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

  • 対称:NULL以外の参照値xおよびyの場合、x.equals(y)は、y.equals(x)がtrueを返す場合にのみtrueを返す必要があります。

  • 推移的:NULL以外の参照値xyzの場合、x.equals(y)trueを返し、y.equals(z)trueを返す場合、x.equals(z)trueを返す必要があります。

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

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

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

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

  2. instanceof演算子を使用して、引数のタイプが正しいかどうかを確認します。そうでない場合は、falseを返します。通常、正しい型はメソッドが発生するクラスです。場合によっては、このクラスによって実装されるインターフェイスがあります。クラスが等号コントラクトを改良するインターフェースを実装する場合は、インターフェースを使用して、インターフェースを実装するクラス間で比較を許可します。 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.NaN-0.0fおよび類似のdouble値の存在により、floatおよびdoubleフィールドの特別な処理が必要になります。 floatフィールドとdoubleフィールドを静的メソッドFloat.equalsおよびDouble.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
    
    }
    
8
user8389458

ageint型であると推測しているため、

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
5
Luchian Grigore

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);
}
4
Peter Walser

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

1
Ashwinee K Jha