기본 패턴
java
public final class User implements Comparable<User> {
private final long id;
private final String email;
public User(long id, String email) {
this.id = id;
this.email = email;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof User other)) return false;
return id == other.id && Objects.equals(email, other.email);
}
@Override
public int hashCode() {
return Objects.hash(id, email);
}
@Override
public int compareTo(User other) {
return Long.compare(this.id, other.id);
}
}설명
==는 두 참조가 같은 객체를 가리키는지 보는 연산이고,equals는 "이 두 객체를 같은 값으로 볼 것인가"를 정의하는 메서드입니다. 입문자가 가장 자주 헷갈리는 지점이 바로 이 차이입니다.equals를 재정의했다면 보통hashCode도 함께 재정의해야 합니다. 이유는HashSet,HashMap처럼 hash 기반 컬렉션이 먼저hashCode로 bucket을 고르고, 그 다음equals로 실제 동일성을 판정하기 때문입니다.Comparable은 "자기 자신의 기본 정렬 기준"을 클래스 안에 넣는 방식입니다. 예를 들어id오름차순을 기본 순서로 삼고 싶다면compareTo를 구현할 수 있습니다.equals와compareTo는 개념적으로 다른 계약입니다.equals는 동등성,compareTo는 정렬 순서입니다. 가능하면 둘이 크게 충돌하지 않도록 설계하는 편이 컬렉션에서 덜 놀랍게 동작합니다.- 실무에서는
record를 쓰면 값 기반 비교를 자동으로 얻을 수 있습니다. 하지만 어떤 필드를 동등성 기준으로 삼을지 더 세밀한 통제가 필요하면 직접 구현하는 편이 낫습니다.
빠른 정리
| 개념 | 무엇을 결정하나 | 자주 연결되는 곳 |
|---|---|---|
== | 같은 참조인가 | 객체 identity 확인 |
equals | 같은 값으로 볼 것인가 | 값 객체, 도메인 비교 |
hashCode | hash 기반 컬렉션 bucket | HashMap, HashSet |
Comparable | 기본 정렬 순서 | Collections.sort, TreeSet |
Comparator | 외부 정렬 기준 | 다양한 정렬 전략 |
주의할 점
equals만 재정의하고 hashCode를 그대로 두면 HashSet이나 HashMap에서 같은 값인데도 중복으로
들어가거나 조회가 실패하는 일이 생길 수 있습니다. 두 메서드는 항상 함께 설계하는 습관이 중요합니다.
참고 링크
3 sources