3章.全オブジェクト共通のメソッド
- この章ではObjectsのメソッドオーバーライド方法を中心にまとめられている。
10.equalsのoverrideは一般契約(general contracts)に従うべし
- 一般契約とは、反射性(reflexive),対照性(symmetric),推移性(transitive),整合性(consistent),非 null 性(non-null)のこと。
- googleのAutoValueを使えば、うまくequalsのオーバーライドを行ってくれる。
11.equalsをオーバーライドする場合はhashcodeもオーバーライドせよ
-
hashCodeの一般契約によると、等しい2つのオブジェクトは等しいhashCodeの値を返さねばならない。よって、equalsをオーバーライドして、論理的に等しいが同じ参照ではない場合も等しいとする場合には、hashCodeをオーバーライドしないままだと異なった値を返すことになってしまう。
-
性能を気にする場合は、hashCodeで計算された値をキャッシュしておく。
12.常にtoStringをオーバーライドせよ
- Thread.javaのtoStringは以下のよう。
public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else {
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
13.cloneをオーバーライドするときは注意せよ
-
super.cloneを呼んでキャストをする。mutableな変数がある場合はdeepcopyして対応する。
-
大体の場合において、cloneメソッドでオブジェクトのコピーを作るよりも、コンストラクタやファクトリメソッドで作るほうが容易である。唯一、配列のコピーはcloneのほうがうまくやれる。
14.Comparableを実装することを考慮せよ
- Javaライブラリの値クラスは、enumも含めておおむねComparableを実装している。
- BigDecimalについて、equalsではBigDecimal("1.0")とBigDecimal("1.00")は異なる扱いになるが、compareToでは同じ扱いになる。そのため、HashSetにこれらの2つを入れたときには、2つの要素が残るが、TreeSetに入れた場合には、1つになる。
- compareToの中で、不等号を使って比較を行うことは冗長で、エラーの要因となりうるので、プリミティブ型のラッパークラスのcompareを使うべき。
- 複数キーでのソートを考える場合、Comparatorのコンストラクションメソッドを使うことを考慮に入れる。1つずつ比較して、if文で分けていくよりもこちらのほうが性能が良いらしい。
package tryAny.effectiveJava;
import static java.util.Comparator.*;
import java.util.Comparator;
import java.util.stream.Stream;
public class CompareTest {
public static void main(String[] args) {
Stream<PhoneNum> s = Stream.of(new PhoneNum(111, 222, 333), new PhoneNum(111, 222, 222),
new PhoneNum(111, 333, 111), new PhoneNum(000, 999, 1));
s.sorted().forEach(System.out::println);
}
}
class PhoneNum implements Comparable<PhoneNum> {
int areaCode;
int prefix;
int lineNum;
public PhoneNum(int areaCode, int prefix, int lineNum) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNum = lineNum;
}
private static final Comparator<PhoneNum> COMPARATOR = comparingInt((PhoneNum pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix).thenComparingInt(pn -> pn.lineNum);
@Override
public int compareTo(PhoneNum pn) {
return COMPARATOR.compare(this, pn);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("areaCode:").append(areaCode).append(",prefix:").append(prefix).append(",lineNum:").append(lineNum);
return sb.toString();
}
}