クラス作成の注意(メソッドのオーバーライド)
toString()はなぜオーバーライドが必要か
toString()メソッドは「インスタンスの内容を、人が読んで理解できる文字列で表現したものとして返す」という責務がある。
自作クラスでtoString()のオーバーライドをしないと親であるObjectクラスのtoString()が呼び出される。
その場合「型名@英数字」という結果になってしまいわかりにくい。
どのようにオーバーライドするか
「今のクラスの中身はどうなっているのだろう」と思ってtoString()を実行したときにわかりやすいような結果を返す
equals()はなぜオーバーライドが必要か
equals()メソッドは「2つの変数に入っているインスタンスが等価であるかを返す」という責務がある。
自作クラスでequals()のオーバーライドをしないと親であるObjectクラスのequals()が呼び出される。
その場合「等値であるか」という意図しない判定になってしまう。
等価と等値の違い
等価は2つのインスタンスが同じ内容であることを表す。
例)インスタンスを指すアドレスが別の場合でも、名前一致や住所一致などで同じ要素と認められれば等価(true)判定をする。
等値は2つのインスタンスが同じアドレスを参照している場合のみtrueとなる。
どのようにオーバーライドするか
クラス開発者がどのような条件であれば等価であるかを判断して判定アルゴリズムを記述する。
以下よくある例
- 自分自身が引数として渡されてきた場合、無条件でtrueを返す(等値)
- nullが引数として渡されてきた場合無条件でfalseを返す
- 比較して型が異なる場合falseを返す
- 2つのインスタンスが持つしかるべきフィールド同士を比較して等価課判定し、true,falseを返す
public class Hoge { String hugaNo; public boolean equals(Object o) { /* 引数はObject型にすること */ if(o == this) return true;//1 if(o == null) return false;//2 if(!(o instanceof Hoge)) return false;//3 Hoge r = (Hoge) o; if(!this.hugaNo.trim().equals(r.hugaNo.trim())) { return false; } //4 return true; } }
hashCode()はなぜオーバーライドが必要か
equals()による計算は比較的大きな計算コストがかかるため、比較的高速なhash系コレクションが動作する場合下記の手順を踏む。
- インスタンスのハッシュ値を比較する(だいたい同じか判定)
- ハッシュ値が同一の要素だけequals()を実行する(厳密に同じか判定)
ハッシュ値とは?
ここでいうハッシュ値は、そのインスタンスの内容を数値として要約したもの。
そのため、
- 等価のインスタンスからは、必ず同じハッシュ値が得られる
- 異なるインスタンスからは、なるべく異なるハッシュ値が得られる
ようにしなければならない。
どのようにオーバーライドするか
class Hoge { String name; int hp; public int hashCode() { int result = 37; /* (1) 適当な初期値を決める(0以外であれば何でもいい) */ result = result * 31 + name.hashCode(); /* (2) 各フィールドの影響を加える(奇数かつ素数の31がよく用いられる) */ result = result * 31 + hp; return result; /* (3) 結果を返す */ } }
LEAVE A REPLY