Eclipseでequals()とhashCode()を生成する

1前書き

この記事では、Eclipse IDEを使って equals() メソッドと hashCode() メソッドを生成する方法を探ります。 Eclipseのコード自動生成がどれほど強力で便利であるかを説明し、さらにコードの入念なテストが必要であることを強調します。

2ルール

Javaの equals() は、2つのオブジェクトが等価かどうかを確認するために使用されます。これをテストする良い方法は、オブジェクトが対称的、再帰的、そして推移的であることを確認することです。つまり、NULL以外の3つのオブジェクト a b 、および c の場合、

  • 対称 - b.equals(a)の場合に限り、a.equals(b)

  • 再帰 - a.equals(a)

  • 推移的 - if a.equals(b) および b.equals(c) の場合 a.equals(c)

hashCode() は1つの規則に従う必要があります。

  • equals() である2つのオブジェクトは、同じ hashCode() 値を持たなければなりません

3プリミティブを持つクラス

プリミティブメンバー変数のみで構成されるJavaクラスを考えてみましょう。

public class PrimitiveClass {

    private boolean primitiveBoolean;
    private int primitiveInt;

   //constructor, getters and setters
}

Eclipse IDEを使って、「Source→ Generate hashCode() and equals() 」を使って equals ()と hashCode ()を生成します。 Eclipseはこのようなダイアログボックスを提供します。

リンク:/uploads/eclipse-equals-hascode-295x300.png%20295w[]

[すべて選択]を選択すると、すべてのメンバー変数を確実に含めることができます。

Insertion Point:の下にリストされているオプションは、生成されたコードのスタイルに影響することに注意してください。ここでは、これらのオプションのいずれも選択せずに[OK]を選択すると、メソッドがクラスに追加されます。

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime **  result + (primitiveBoolean ? 1231 : 1237);
    result = prime **  result + primitiveInt;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    PrimitiveClass other = (PrimitiveClass) obj;
    if (primitiveBoolean != other.primitiveBoolean) return false;
    if (primitiveInt != other.primitiveInt) return false;
    return true;
}

生成された hashCode() メソッドは素数の宣言(31)から始まり、プリミティブオブジェクトに対してさまざまな操作を実行し、オブジェクトの状態に基づいてその結果を返します。

equals() は、2つのオブジェクトが同じインスタンス(==)であるかどうかを最初に確認し、一致する場合はtrueを返します。

次に、比較オブジェクトがnullでなく、両方のオブジェクトが同じクラスであることを確認し、そうでない場合はfalseを返します。

最後に、 equals() は各メンバ変数の等価性をチェックし、いずれかが等しくない場合はfalseを返します。

簡単なテストを書くことができます。

PrimitiveClass aObject = new PrimitiveClass(false, 2);
PrimitiveClass bObject = new PrimitiveClass(false, 2);
PrimitiveClass dObject = new PrimitiveClass(true, 2);

assertTrue(aObject.equals(bObject) && bObject.equals(aObject));
assertTrue(aObject.hashCode() == bObject.hashCode());

assertFalse(aObject.equals(dObject));
assertFalse(aObject.hashCode() == dObject.hashCode());

4コレクションとジェネリックスを持つクラス

それでは、コレクションと総称を含む、より複雑なJavaクラスを考えてみましょう。

public class ComplexClass {

    private List<?> genericList;
    private Set<Integer> integerSet;

   //constructor, getters and setters
}

Eclipseの「Source→ Generate hashCode() and equals()」を使用します。 hashCode() は、クラスオブジェクトの比較に instanceOf__を使用しています。ダイアログ我々が得る:

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime **  result + ((genericList == null)
      ? 0 : genericList.hashCode());
    result = prime **  result + ((integerSet == null)
      ? 0 : integerSet.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (!(obj instanceof ComplexClass)) return false;
    ComplexClass other = (ComplexClass) obj;
    if (genericList == null) {
        if (other.genericList != null)
            return false;
    } else if (!genericList.equals(other.genericList))
        return false;
    if (integerSet == null) {
        if (other.integerSet != null)
            return false;
    } else if (!integerSet.equals(other.integerSet))
        return false;
    return true;
}

生成された hashCode()メソッドは、 AbstractList.hashCode() および AbstractSet.hashCode() コアJavaメソッドに依存しています。これらはコレクションを繰り返し処理し、各項目の hashCode()__値を合計して結果を返します。

同様に、生成された equals() メソッドは AbstractList.equals() AbstractSet.equals() を使用します。これらは、フィールドを比較することによってコレクションの等価性を比較します。

いくつかの例をテストすることで、堅牢性を検証できます。

ArrayList<String> strArrayList = new ArrayList<String>();
strArrayList.add("abc");
strArrayList.add("def");
ComplexClass aObject = new ComplexClass(strArrayList, new HashSet<Integer>(45,67));
ComplexClass bObject = new ComplexClass(strArrayList, new HashSet<Integer>(45,67));

ArrayList<String> strArrayListD = new ArrayList<String>();
strArrayListD.add("lmn");
strArrayListD.add("pqr");
ComplexClass dObject = new ComplexClass(strArrayListD, new HashSet<Integer>(45,67));

assertTrue(aObject.equals(bObject) && bObject.equals(aObject));
assertTrue(aObject.hashCode() == bObject.hashCode());

assertFalse(aObject.equals(dObject));
assertFalse(aObject.hashCode() == dObject.hashCode());

5継承

継承を使うJavaクラスを考えましょう。

public abstract class Shape {
    public abstract double area();

    public abstract double perimeter();
}

public class Rectangle extends Shape {
    private double width;
    private double length;

    @Override
    public double area() {
        return width **  length;
    }

    @Override
    public double perimeter() {
        return 2 **  (width + length);
    }
   //constructor, getters and setters
}

public class Square extends Rectangle {
    Color color;
   //constructor, getters and setters
}

Square クラスで 'Source→ Generate hashCode() equals() を試みた場合、Eclipseは’スーパークラス 'Rectangle’が equals() hashCode() を再宣言しないことを警告します。正しく機能しない可能性があります。

同様に、 Rectangle クラスで hashCode() および equals() を生成しようとすると、スーパークラスShapeに関する警告が表示されます。

Eclipseは警告にもかかわらず私たちが前進することを可能にするでしょう。 Rectangle の場合、具象メンバ変数がないため、 hashCode() または equals() を実装できない抽象 Shape クラスを拡張します。

その場合はEclipseを無視できます。

ただし、 Square クラスは、Rectangleから width および length メンバ変数、およびそれ自身の色変数を継承します。 Rectangle に対して最初に同じことを行わずに Square hashCode() および equals() を作成することは、 equals() / hashCode() color のみを使用することを意味します。

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime **  result + ((color == null) ? 0 : color.hashCode());
    return result;
}
@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    Square other = (Square) obj;
    if (color == null) {
        if (other.color != null)
            return false;
    } else if (!color.equals(other.color))
        return false;
    return true;
}

簡単なテストでは、 width equals() / hashCode() の計算に含まれていないため、 Square equals() / hashCode() width のみで異なる場合は不十分であることがわかります。

Square aObject = new Square(10, Color.BLUE);
Square dObject = new Square(20, Color.BLUE);

Assert.assertFalse(aObject.equals(dObject));
Assert.assertFalse(aObject.hashCode() == dObject.hashCode());

Eclipseを使用して Rectangle クラスの equals() / hashCode() を生成して、これを修正しましょう。

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    long temp;
    temp = Double.doubleToLongBits(length);
    result = prime **  result + (int) (temp ^ (temp >>> 32));
    temp = Double.doubleToLongBits(width);
    result = prime **  result + (int) (temp ^ (temp >>> 32));
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    Rectangle other = (Rectangle) obj;
    if (Double.doubleToLongBits(length)
      != Double.doubleToLongBits(other.length)) return false;
    if (Double.doubleToLongBits(width)
      != Double.doubleToLongBits(other.width)) return false;
    return true;
}

Square クラスで equals() / hashCode() を再生成する必要があるので、 Rectangle ´s equals() / hashCode() が呼び出されます。この世代のコードでは、Eclipseダイアログのすべてのオプションを選択したので、コメント、 instanceOf 比較、および if ブロックが表示されます。

@Override
public int hashCode() {
    final int prime = 31;
    int result = super.hashCode();
    result = prime **  result + ((color == null) ? 0 : color.hashCode());
    return result;
}


@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (!super.equals(obj)) {
        return false;
    }
    if (!(obj instanceof Square)) {
        return false;
    }
    Square other = (Square) obj;
    if (color == null) {
        if (other.color != null) {
            return false;
       }
    } else if (!color.equals(other.color)) {
        return false;
    }
    return true;
}

Square 's s hashCode() / equals() は正しく計算されているので、上からテストを再実行して、今すぐパスします。

6. 結論

Eclipse IDEは非常に強力で、定型コードの自動生成を可能にします - ゲッター/セッター、さまざまな型のコンストラクター、 equals() 、および hashCode()

Eclipseが何をしているのかを理解することで、これらのコーディング作業に費やす時間を減らすことができます。ただし、予想されるすべてのケースを確実に処理できるように、注意を払いながらコードをテストで検証する必要があります。

コードスニペットは、いつものようにhttps://github.com/eugenp/tutorials/tree/master/core-java-lang[over on GitHub]にあります。