読者です 読者をやめる 読者になる 読者になる

日々常々

ふつうのプログラマがあたりまえにしたいこと。

equalsだけをオーバーライド

何と比べても同じって言うクラスAがあって。

class A {
    @Override
    public boolean equals(Object obj) {
        return true;
    }
}

こうすると?

Collection set = new HashSet();
Collection list = new ArrayList();

set.add(new A());
set.add(new A());
list.add(new A());
list.add(new A());

System.out.println(set.contains(new A()));
System.out.println(list.contains(new A()));

System.out.println(set.size());
System.out.println(list.size());

こうなった。

false
true
2
2

実際に動かしてもこの通り動作するとおもいます。ですけど、newした時にhashCodeが違う値になる保証は無い*1ので問題としては成立してなかったり…。

少なくともこの問題はEffectiveJavaの「項目9 equalsをオーバーライドする時は、常にhashCodeをオーバーライドする」に真っ向から違反すると、色々まともに動かなくなります。
例で挙げている Collection#contains には以下のように書かれています。

コレクションに指定された要素がある場合に true を返します。すなわち、このコレクションに (o==null ? e==null : o.equals(e)) である要素 e が 1 つ以上ある場合にだけ true を返します。

この場合は条件を満たすのに false になってしまってるわけです。前のも合わせて equals/hashCodeはちゃんと実装しましょうって事になるんですけどね。

おまけ

class A {
    public boolean equals(Object obj) {return true;}
    public int hashCode() {return 1;}
}
class B {
    public boolean equals(Object obj) {return true;}
    public int hashCode() {return 1;}
}
Set set = new HashSet();
set.add(new B());
set.add(new A());

for (Object o : set) {
    System.out.println(o.getClass());
}

答えはー……わからなかったら動かしてください。あ、ちょっと動かす時にはGroovyConsoleって言うのが便利ですよ!

*1:Object#hashCodeには「できるかぎり、Object クラスで定義される hashCode メソッドは、異なるオブジェクトについては異なる整数値を返します。」と書かれてるに過ぎなくて。