Object.equals()
2025. 4. 23. 16:43ㆍHigh Level Programming Language/Learning the Java Language
🔍 1. 객체의 동일성 vs 동등성 – JVM 메모리 관점
✅ 객체의 동일성 (Identity)
==
연산자는 두 레퍼런스가 같은 힙 객체를 가리키는지를 비교합니다.- 즉,
System.identityHashCode()
가 같을 가능성이 높고, 힙 메모리 상의 주소가 동일하다는 것을 의미합니다. - JVM 수준에서 동일한 object header를 공유하게 됩니다.
A a1 = new A();
A a2 = a1;
System.out.println(a1 == a2); // true
✅ 객체의 동등성 (Equality)
equals()
는 논리적으로 같은지(동등한 값인지)를 비교합니다.- 두 객체가 서로 다른 힙 주소를 갖고 있어도, 논리적으로 같다고 판단할 수 있습니다.
- 이 비교는 대부분 객체 내부의 필드 상태를 기준으로 수행됩니다.
A a1 = new A("foo");
A a2 = new A("foo");
System.out.println(a1 == a2); // false
System.out.println(a1.equals(a2)); // true (논리적 동등성)
🧠 2. equals()
의 설계 철학 – 왜 오버라이딩이 필요한가?
✅ Object 클래스의 기본 equals()
정의
public boolean equals(Object obj) {
return (this == obj);
}
- 즉, 기본
equals()
는 단순 참조 비교이며, 재정의하지 않으면==
와 동일합니다. - 하지만 대부분의 경우, 객체를 값 기반 비교(Value Object)로 사용하므로 오버라이딩이 필요합니다.
- 자바는 값 기반 설계(Value-based Class)를 지향하는 경우
equals()
를 반드시 구현하도록 유도합니다.
⚖️ 3. equals()의 계약(Contract)과 수학적 성질
자바는 equals()
메서드를 단순 비교 함수가 아닌, 엄격한 수학적 이항 관계로 간주합니다. 이로 인해 다음과 같은 5가지 규칙을 반드시 동시에 만족해야 합니다:
계약 | 설명 |
---|---|
Reflexive | x.equals(x) 는 항상 true여야 함 |
Symmetric | x.equals(y) 가 true이면 y.equals(x) 도 true여야 함 |
Transitive | x.equals(y) && y.equals(z) 이면 x.equals(z) 여야 함 |
Consistent | 여러 번 호출해도 결과가 동일해야 함 (외부 상태 변화가 없다면) |
Null-safe | x.equals(null) 은 항상 false여야 함 |
❗ 계약을 어기면?
- 컬렉션에서 이상 동작이 발생합니다.
- 예:
HashSet
에 같은 객체를 여러 개 넣을 수 있음. - 예:
Map.get()
이 동작하지 않음.
🛠️ 4. equals()
오버라이딩의 전략적 구현
✅ 고전적 구현 템플릿 (Effective Java 권장 패턴)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyClass other = (MyClass) o;
return age == other.age && Objects.equals(name, other.name);
}
🔎 주요 고려 사항
getClass() != o.getClass()
는 대칭성을 보장 (instanceof는 다형성을 허용하므로 조심)Objects.equals(a, b)
를 사용하면 NPE 방지 가능hashCode()
반드시 함께 오버라이딩
🧩 5. equals(), hashCode(), compareTo()의 관계
메서드 | 용도 | 주의할 점 |
---|---|---|
equals() |
논리적 동등성 비교 | 계약 준수 필요 |
hashCode() |
해시 기반 컬렉션의 버킷 결정 | equals() 와 함께 오버라이딩 |
compareTo() |
정렬 기준 | equals() 와 일관성 유지 권장 |
❗ compareTo()
와 equals()
의 결과가 다르면 TreeSet
, TreeMap
에서 버그가 발생할 수 있습니다.
🔐 6. equals()
vs ==
vs Objects.equals()
비교 | 설명 |
---|---|
== |
동일성 (same reference) |
equals() |
동등성 (오버라이딩 필요) |
Objects.equals(a, b) |
null-safe equals 비교 (a가 null이어도 NPE 발생 안 함) |
String a = null;
String b = "hello";
System.out.println(Objects.equals(a, b)); // false
System.out.println(a.equals(b)); // NPE 발생
🧠 7. 자주 발생하는 실수와 교훈
❌ equals()만 오버라이딩하고 hashCode()는 그대로 둘 경우
HashSet
이나HashMap
에서 정상 동작하지 않음
Set<MyClass> set = new HashSet<>();
set.add(new MyClass("a", 1));
set.contains(new MyClass("a", 1)); // false
✅ 항상 equals와 hashCode는 세트로 생각하기
@Override
public int hashCode() {
return Objects.hash(name, age);
}
🧪 8. equals()의 내부 최적화
- HotSpot JVM은
equals()
호출 시 인라인 캐싱(Inline Caching)과 Escape Analysis를 통해 성능 최적화 수행 ==
연산자보다 성능은 느릴 수 있지만, 의미상 더 강력하고 설계 유연성 높음
🔚 결론: 요약
equals()
는 단순 비교 함수가 아니라 객체 동등성 계약을 따르는 수학적 관계 메서드==
은 JVM 메모리 주소 참조 비교,equals()
는 의미론적(semantic) 값 비교- 컬렉션, 멀티스레딩, 디자인 패턴(예: Proxy, Value Object)에서 매우 중요한 요소
- 오버라이딩 시 5대 계약 원칙,
hashCode()
와의 동기화, 대칭성/추이성 테스트까지 반드시 수행 - 잘못된 equals 구현은 컬렉션, 캐시, ORM에서 치명적인 버그 유발
차후 확인할 사항들:
- IntelliJ에서
equals/hashCode
자동 생성시 옵션 차이 분석 - Lombok의
@EqualsAndHashCode
vs 수동 오버라이딩 비교 - equals 계약을 검증하는 유닛 테스트 방법 (
EqualsVerifier
사용)
'High Level Programming Language > Learning the Java Language' 카테고리의 다른 글
Prime Number 31 (0) | 2025.04.23 |
---|---|
equals() 메서드의 5대 계약 원칙 (0) | 2025.04.23 |
POJO vs JavaBean (0) | 2025.04.23 |
JavaBean의 Property란? (0) | 2025.04.23 |
자바의 오버라이딩과 공변 리턴 타입(Covariant Return Type) 완벽 가이드 (1) | 2025.04.23 |