Object#equals()の作法
Object#equals()はオブジェクト間の等価性を定義するためのメソッドである。そのため、不適切な定義を行えば、java.util.collectionなど、様々なフレームワークが破綻する。そのため、あらゆるフレームワークに対応するような定義について考える。
- Object#equals()は副作用を持たない。
- Object#equals()は反射律 objectA.equals(objectA) を満たす。
- Object#equals()は対称律 objectA.equals(objectB)==objectB.equals(objectA) を満たす。
- 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() とするのが一般的である。