こんなequalsは嫌だ

22
大大 EffectiveJava 大大大 大 2 大 2011/06/18 大 大 こここ equals/hashCode こここ

Upload: oda-shinsuke

Post on 28-May-2015

1.035 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: こんなEqualsは嫌だ

大阪 EffectiveJava 読書会 第 2 回2011/06/18 お だ

こんな equals/hashCode

は嫌だ

Page 2: こんなEqualsは嫌だ

織田 信亮 ( おだ しんすけ )大阪で 開発者 してます。MS 系の情報を追っかけてますId:odashinsuke @shinsukeodaSQL World (SQL Server のコミュニティ )

日本 Grails/Groovy ユーザグループ

自己紹介

Page 3: こんなEqualsは嫌だ

第 3 章 項目 8 equals を… P.33反射的

x.equals(x)対称的

y.equals(x) ⇒ x.equals(y)推移的

x.equals(y) && y.equals(z) ⇒ x.equals(z)整合的

x.equals(y) ⇒ x.equals(y)

Page 4: こんなEqualsは嫌だ

文字に置き換えると…反射的

A = A対称的

A = B ⇒ B = A推移的

A = B & B = C ⇒ A = C整合的

上手く表せない orz...

Page 5: こんなEqualsは嫌だ

final class 反射的 {

public int id;

@Override

public boolean equals(Object obj) {

if (!(obj instanceof 反射的 )) { return false; }

return this.id == (( 反射的 )obj).id + 1;

}

}

反射的 x = new 反射的 ();

x.id = 1;

System.out.println("x.equals(x):" + x.equals(x)); // false

反射的 でない!

Page 6: こんなEqualsは嫌だ

final class 対称的 {

public int value;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (obj == null) { return false; }

try {

Field f = obj.getClass().getDeclaredField("value");

f.setAccessible(true);

Object objId = f.get(obj);

return this.value == (Integer)objId;

} catch (Throwable e) { // ガン無視! return false;

}

}

}

対称的 でない!

Page 7: こんなEqualsは嫌だ

対称的 x = new 対称的 ();

x.value = 1;

Integer y = Integer.valueOf(1);

System.out.println("x.equals(y):" + x.equals(y)); // true

System.out.println("y.equals(x):" + y.equals(x)); // false

対称的 でない!

Page 8: こんなEqualsは嫌だ

class Super推移的 { public int id;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (!(obj instanceof Super推移的 )) { return false; }

return this.id == ((Super推移的 )obj).id;

}

}

final class Sub 推移的 extends Super推移的{

public int seq;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (!(obj instanceof Super推移的 )) { return false; }

if (obj instanceof Sub 推移的 ) {

return this.id == ((Sub 推移的 )obj).id &&

this.seq == ((Sub 推移的 )obj).seq;

}

return super.equals(obj);

}

}

推移的 でない!

Page 9: こんなEqualsは嫌だ

Sub 推移的 x = new Sub 推移的 ();

x.id = 1;

x.seq = 1;

Super 推移的 y = new Super 推移的 ();

y.id = 1;

Sub 推移的 z = new Sub 推移的 ();

z.id = 1;

z.seq = 2;

System.out.println(“x.equals(y):” + x.equals(y)); // true

System.out.println("y.equals(z):" + y.equals(z)); // true

System.out.println("x.equals(z):" + x.equals(z)); // false

推移的 でない!

Page 10: こんなEqualsは嫌だ

final class 整合的 {

public int id;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (!(obj instanceof 整合的 )) { return false; }

return this.id++ == (( 整合的 )obj).id;

}

}

整合的 x = new 整合的 ();

x.id = 1;

整合的 y = new 整合的 ();

y.id = 1;

System.out.println("x.equals(y):" + x.equals(y)); // true

System.out.println("x.equals(y):" + x.equals(y)); // false

整合的 でない!

Page 11: こんなEqualsは嫌だ

final class 例外 {

public int id;

@Override

public boolean equals(Object obj) {

return this.id == (( 例外 )obj).id;

}

}

例外 x = new 例外 ();

x.id = 1;

try {

System.out.println(x.equals(null));

} catch (Throwable e) {

System.out.println(“equals で例外とかありえなーい” ); // こっち通る}

例外を吐く

Page 12: こんなEqualsは嫌だ

final class まとも {

public int id;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (!(obj instanceof まとも )) { return false; }

return this.id == (( まとも )obj).id;

}

@Override

public int hashCode() { return this.id; }

}

まとも

Page 13: こんなEqualsは嫌だ

まとも x = new まとも ();

x.id = 1;

まとも y = new まとも ();

y.id = 1;

まとも z = new まとも ();

z.id = 1;

System.out.println("x.equals(x):" + x.equals(x)); // true

System.out.println("x.equals(y):" + x.equals(y)); // true

System.out.println("y.equals(x):" + y.equals(x)); // true

System.out.println("x.equals(y):" + x.equals(y)); // true

System.out.println("y.equals(z):" + y.equals(z)); // true

System.out.println("x.equals(z):" + x.equals(z)); // true

System.out.println("x.equals(y):" + x.equals(y)); // true

System.out.println("x.equals(y):" + x.equals(y)); // true

まとも

Page 14: こんなEqualsは嫌だ

第 3 章 項目 9 equals を… 常に hashCode を… P.45

Object の状態が変わらないなら、 hashCode は常に同じ値を返す

equals での比較結果が true なら、 hashCode を比較しても同じになる

equals での比較結果が false でも、 hashCode が同じになっても良い

Page 15: こんなEqualsは嫌だ

final class まとも {

public int id;

@Override

public boolean equals(Object obj) {

if (this == obj) { return true; }

if (!(obj instanceof まとも )) { return false; }

return this.id == (( まとも )obj).id;

}

@Override

public int hashCode() { return this.id; }

}

まとも

Page 16: こんなEqualsは嫌だ

正しいですね!

Page 17: こんなEqualsは嫌だ

項目 10 GetHashCode() の罠に注意する P.56

こんな内容のことが書いています

ハッシュコンテナのキーとして使用されることになる値型を作成する場合、その型は不変型にすべき。

Page 18: こんなEqualsは嫌だ

??

Page 20: こんなEqualsは嫌だ

まとも x = new まとも ();

x.id = 1;

HashMap< まとも , String> map =

new HashMap< まとも , String>();

map.put(x, "A");

System.out.println(map.get(x)); // A

x.id = 3;

System.out.println(map.get(x)); // null

HashMap で利用すると…

Page 21: こんなEqualsは嫌だ

まとめ

Page 22: こんなEqualsは嫌だ

Hash の KEY として利用するなら、少なくとも hashCode に利用するフィールドは、 Immutable( 不変 ) が良さそう。

って、中々 Hash の KEY には使わないよね…。