Object#equals()の作法

Object#equals()はオブジェクト間の等価性を定義するためのメソッドである。そのため、不適切な定義を行えば、java.util.collectionなど、様々なフレームワークが破綻する。そのため、あらゆるフレームワークに対応するような定義について考える。

  1. Object#equals()は副作用を持たない。
  2. Object#equals()は反射律 objectA.equals(objectA) を満たす。
  3. Object#equals()は対称律 objectA.equals(objectB)==objectB.equals(objectA) を満たす。
  4. Object#equals()は推移律 objectA.equals(objectB)∧objectB.equals(objectC)⇒objectA.equals(objectC) を満たす。

実例1.変数同士の参照先が等しい。
Ref.java

public class Ref.java{
    boolean equals(Object obj){
        return this == obj;
    }
}

実例2.オブジェクト同士のフィールドの参照先が等しい。
Shallow.java

public class Shallow.java{
    Object field;
    boolean equals(Object obj){
        if(obj instanceof Shallow) {
            return this.field == ((Shallow)obj).field;
        }
        return false;
    }
}

実例3.オブジェクト同士のフィールドが再帰的に等しい。
Deep.java

public class Deep.java{
    Object field;
    boolean equals(Object obj){
        if(obj instanceof Deep) {
            return this.field.equals(((Deep)obj).field);
        }
        return false;
    }
}

Object#equals()が適切に定義されていれば、以下のようにcopy()や副作用を定義できる。しかし、残念なことに副作用の定義が再帰しており不完全である。やはり、副作用を持たないことの定義は外部的に行うべきなのだろうか。

  • copy()は objectA.equals(objectA.copy()) を満たす。*1
  • method()が副作用を持たないとき、↓は常にtrueを返す。
boolean testMethod(Object objectA) {
    Object copyA = objectA.copy();
    objectA.method();
    return objectA.equals(copyA);
}

*1:実用上は objectA!=objectA.copy() とするのが一般的である。