Objectクラスに定義されている数少ないメソッドの中で、
これら二つはオーバーロード(上書き)する機会がたまにあります。
ハッシュマップのキーに使うクラスには、必ずこれを実装する必要があります。
ここで大事なことは、「これらのメソッド両方をオーバーロードしなければならない」
という点です。
両者が「同値」のとき必ずtrueを返し、それ以外のときは必ずfalseを返すようにします。
同値オブジェクトは必ず同じ値を返す必要があります。
同値でないオブジェクト同士は、出来る限り異なる値を返した方が良いのですが
それは必須ではありません。
PMD : OverrideBothEqualsAndHashcode
CheckStyle : EqualsHashCode
ハッシュコードは、オブジェクトの内容が変化しない限り
同じ値を取ります。
従って、一度計算したハッシュコードはオブジェクト内に保持して
次回の同メソッド呼出時にはそれを返すようにします。
ハッシュマップは頻繁にこのメソッドを呼び出すので、
毎回ハッシュコードを計算していたのではパフォーマンスが落ちるのです。
オブジェクトの内容が変化したときには、保持しているハッシュコードをクリアします。
その為、ハッシュコードが計算されていない状態が 0 を示すようにします。
例えば Foo クラスに equals メソッドを実装するとき、
public boolean equals(Foo o) {
...
}
ついこのようなコードを書いてしまいがちですが、これは間違っています。
正しくは、
public boolean equals(Object o) {
...
}
です。equals メソッドの引数は必ず Object 型にしないといけません。
そうしないと、Object.equals() をオーバーライドすることにはならず
全く関係の無い equals メソッドが定義されただけだと認識されてしまいます。
CheckStyle : CovariantEquals
FindBugs : Eq: Covariant equals() method defined
BigDecimalにはdouble値を引数に取るコンストラクタがありますが
これを使ってはいけません。
new BigDecimal(4.15); -> new BigDecimal("4.15");
前者の方では、精度が失われる可能性があります。
PMD : AvoidDecimalLiteralsInBigDecimalConstructor
Javaでは、コンストラクタ内で自動的に super() が呼び出されます。
public class A {
public A() {
...
}
public A(int n) {
...
}
}
public class B extends A {
public B() {
// 何も記述しなくても、A() の処理が実行される
}
public B(int n) {
// 何も記述しなくても、A() の処理が実行される
// しかし、実際には A(int) の処理を実行させたいはず
}
}
デフォルトの処理に頼っていると、オーバーロードしたコンストラクタ内で
ミスが発生しやすくなります。
ですので、明示的に super() と記述しておくことが
良いコーディングスタイルとされているようです。
PMD : CallSuperInConstructor
Javaではメンバのスコープをいくつかの段階で定義できます。
アクセス識別子を省略すると、そのメンバは
同一パッケージ内からのアクセスのみ許可されます。
同一クラスからのアクセスのみ許可されます。
同一クラスおよびその派生クラスからのアクセスのみ許可されます。
全クラスからのアクセスが許可されます。
このうち、パッケージprivateはそのスコープ範囲が
明確でないという理由であまり推奨されていません。
また、フィールドを protected にするのも同様に勧められません。
PMD : DefaultPackage
CheckStyle : VisibilityModifier
package宣言をしないクラスは、デフォルトパッケージに所属することになります。
これは一時的なテスト用にはいいかもしれませんが
一般的にはあまり使わない方がいいとされています。
PMD : DefaultPackage
CheckStyle : PackageDeclaration
数値などのプリミティブ型の比較には == 演算子を使いますが
オブジェクトの比較には通常 equals() を使います。
これらがどのように違うかというのは、なかなか説明するのが難しいのですが
例を挙げて簡単に説明します。
int x = 10;
int y = 10;
if (x == y) {
// 当然 x == y であり、真となる
}
Car car1 = new Car("Type-A");
Car car2 = new Car("Type-A");
if (car1 == car2) {
// car1, car2 ともに同じ内容であるが、真にはならない
}
if (car1.equals(car2)) {
// car1 と car2 は共にType-Aであり、同値なので真になる
}
このように、オブジェクトの場合
両者が「同一」である事と「同値」である事を区別する必要があります。
同一である場合に限って == 演算子は真を返します。
equals() は、同一でなくても同値ならば真を返します。
見ての通り equals() はメソッドであり、オーバーライド可能な為
「同値」である判断基準はプログラムレベルで変更することが出来ます。
それに対し「同一」である判断基準は変更することが不可能です。
ほとんどの場合、オブジェクトの同一性を判定する場面はありません。
よって、オブジェクトの比較には == ではなく equals() を使います。
さらに言えば、Object.equals() の実装は
public boolean equals(Object o) {
return this == o;
}
となっていますので、オブジェクトの同一性を判定したい場合は
equals() メソッドをオーバーライドせずに呼び出せば良いという事になるので
全ての場面において == 演算子を使う必要が無いという事です。
また、a.equals(b) という処理において、どちらをaにしてもbにしてもいいのですが
もし定数値があったらそれを迷わずaにしましょう。
a が null である可能性がある場合には、事前に null チェックをする必要があります。
PMD : CompareObjectsWithEquals / PositionLiteralsFirstInComparisons
CheckStyle : StringLiteralEquality
FindBugs : ES: Comparison of String objects using == or != / RC: Suspicious reference comparison
Vector や Hashtable は、JDK1.2以前で使われていたクラスで
現在はその使用が奨められていません。
代わりに ArrayList や HashMap を使用しましょう。
CheckStyle : IllegalType
配列の比較には equals() メソッドが使えません。
代わりに java.util.Arrays.equals(Object[], Object[]) を使用します。
FindBugs : EC: Invocation of equals() on an array,which is equivalent to ==
シリアライズ可能なクラスには、非直列化可能なフィールドを定義してはいけません。
シリアライズしようとした段階で例外が発生してしまいます。
class A implements Serializable {
private Iterator it;
// Iteratorは非直列化フィールドなので、正しくシリアライズされない可能性がある。
}
この場合の対処法としては、非直列化フィールドをtransientにするか
独自の read/writeObject メソッドを実装する方法があります。
FindBugs : Se: Non-transient non-serializable instance field in serializable class
FindBugs : Se: Class is Serializable but its superclass doesn't define a void constructor
Javaでは、クラスにコンストラクタを定義していない場合に
引数無しのデフォルトコンストラクタが暗黙的に作成されます。
public class A {
public void bar() {
A obj = new A();
...
}
}
このような場合、一つコンストラクタを作成すると
デフォルトコンストラクタは「作成されません」。
つまり…
public class A {
public A(int v) {
...
}
public void bar() {
A obj = new A(); // コンパイルエラー
...
}
}
こうなってしまいます。
ですから、デフォルトコンストラクタに頼ったコーディングというのは推奨されません。
PMD : AtLeastOneConstructor
CheckStyle : MissingCtor