AIによる要約
Javaでequalsをオーバーライドするときは、単にIDEで生成すればよいわけではありません。何を同じとみなすのか、hashCodeと揃っているか、変更される値を比較に使っていないかを確認する必要があります。


equalsは、オブジェクト同士を「同じものとして扱うか」を決めるメソッドです。
現場では、ID、コード値、注文番号など、業務上の同一性を表すクラスでよく話題になります。
この記事のポイント
equalsは同一判定のルールを表す- オーバーライドするなら
hashCodeもセットで考える - 比較に使うフィールドは業務上の意味で決める
- 可変フィールドを比較に入れるとSetやMapで事故りやすい
equalsは何を同じとみなすかのルール
初学者のうちは、equalsを「オブジェクトを比較するメソッド」とだけ覚えがちです。しかし実務では、もう一歩踏み込んで「何を同じとみなすか」を決める設計になります。
public class ProductCode {
private final String value;
public ProductCode(String value) {
this.value = Objects.requireNonNull(value);
}
}
このProductCodeであれば、valueが同じなら同じ商品コードとして扱いたいはずです。つまり、equalsの基準はvalueになります。
まず守るべき基本形
equalsを手書きする場合は、自己比較、型チェック、フィールド比較の順で考えると読みやすくなります。

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof ProductCode other)) {
return false;
}
return Objects.equals(this.value, other.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
ここでもequalsとhashCodeはセットです。比較に使ったvalueと、ハッシュ値に使うvalueを揃えます。
注意点1:比較フィールドを何となく選ばない
IDEやLombokで生成するとき、フィールドを全部選んでしまうことがあります。しかし、業務上の同一性とすべてのフィールドが一致することは別です。
public class User {
private Long id;
private String name;
private String email;
private LocalDateTime lastLoginAt;
}
このクラスでユーザーを同一判定したい場合、idだけを見るのか、メールアドレスも見るのか、ログイン日時まで見るのかで意味が変わります。lastLoginAtを比較に含めると、ログインするたびに別ユーザー扱いになるような危険があります。
比較フィールドを選ぶ観点
- 業務上、何が同じなら同じものと言えるか
- その値は作成後に変わらないか
- 画面表示用の名前や日時を同一判定に含めてよいか
- DBの主キー、業務キー、画面入力値のどれを基準にするか
注意点2:変更される値を比較に入れない
SetやMapのキーにするクラスでは、比較に使う値が後から変わると危険です。入れたときと探すときでハッシュ値が変わると、取り出せなくなることがあります。
public class UserKey {
private String userId;
public void setUserId(String userId) {
this.userId = userId;
}
@Override
public int hashCode() {
return Objects.hash(userId);
}
}
このようなクラスをMapのキーにするなら、userIdを後から変更できない設計にする方が安全です。
注意点3:Entityのequalsはプロジェクト方針を見る
JPAのEntityでは、equalsの扱いが難しくなります。IDが採番前はnullだったり、遅延ロードやプロキシが絡んだりするためです。
新人のうちは、Entityに対して自分の判断だけでequals/hashCodeを追加しない方が安全です。既存Entityの書き方、チームの規約、レビュー担当者の方針を確認しましょう。
// レビューコメント例 // Entityにequals/hashCodeを追加していますが、プロジェクトではEntityの同一判定をIDベースに統一しています。 // 採番前の扱いもあるため、既存Entityと同じ方針に合わせてください。
注意点4:自動生成しても意味は説明できるようにする
IDE生成やLombokを使えば、コード自体は短くできます。しかし、レビューでは「なぜそのフィールドを比較に使ったのか」を説明できるかが見られます。
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class ProductCode {
@EqualsAndHashCode.Include
private final String value;
}
このように明示的に含めるフィールドを指定すると、意図が読み手に伝わりやすくなります。自動生成を使う場合でも、設計判断は人間が行います。
レビュー前のセルフチェック
equalsオーバーライドの確認リスト
equalsとhashCodeをセットで実装しているか- 比較フィールドを業務上の同一性で説明できるか
- 変更される値を比較に入れていないか
- EntityやDTOでプロジェクト方針に反していないか
- テストで同じケースと違うケースを確認したか
特に、同じ値なら同じになるテストだけでなく、違う値なら違うになるテストも入れておくと安心です。
ProductCode code1 = new ProductCode("P001");
ProductCode code2 = new ProductCode("P001");
ProductCode code3 = new ProductCode("P002");
assertThat(code1).isEqualTo(code2);
assertThat(code1).isNotEqualTo(code3);
この記事とあわせて読みたい
まとめ
equalsのオーバーライドは、ただの定型コードではありません。何を同じとみなすかを決める設計です。自動生成を使う場合でも、比較フィールド、変更可能性、hashCodeとの整合性を説明できる状態にしてからレビューに出しましょう。