10. equals는 일반 규약을 지켜 재정의하라
Effective Java Item 10
equals는 일반 규약을 지켜 재정의하라
이 문서는 Effective Java의 아이템 10, "equals는 일반 규약을 지켜 재정의하라"를 주제로 객체의 동등성을 올바르게 비교하는 방법에 대해 다룹니다.
객체 지향 프로그래밍에서 equals 메서드는 객체의 논리적 동등성을 판단하는 중요한 역할을 합니다. 하지만 equals 메서드를 잘못 재정의하면 예상치 못한 동작을 초래하고, 코드의 유지보수성을 저하시킬 수 있습니다. 따라서 equals 메서드를 재정의할 때는 반드시 일반 규약을 준수해야 합니다.
equals를 재정의하지 말아야 할 경우
각 인스턴스가 본질적으로 고유하다
Thread thread1 = new Thread();
Thread thread2 = new Thread();
System.out.println(thread1.equals(thread2)); // false (객체 식별성 비교)값이 아닌 동작하는 개체 표현 (Thread, Activity, Service)
Object의 기본 equals가 정확한 동작 수행
인스턴스의 논리적 동치성을 검사할 일이 없다
설계자가 의도적으로 논리적 비교를 배제한 경우
Singleton, Enum ...
상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다.
대부분의 Set/List 구현체는 AbstractSet/AbstractList의 equals 상속
클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다
실수로 호출되는 걸 막고 싶다면 구현해두자
equals()를 재정의해야 하는 경우
논리적 동치성을 확인해야 하는데, equals가 그렇게 재정의되지 않았을 때
논리적 동등성을 비교해야 하는 값 객체 (ex. Integer, String)
컬렉션의 키(Key)로 사용될 객체 (ex. HashMap, HashSet의 요소)
동등성 비교가 클래스의 주요 기능일 때
equals() 규약 위반 시 발생할 수 있는 문제점
컬렉션 프레임워크(HashSet, HashMap)에서 예기치 않은 동작 발생
동등성을 기반으로 동작하는 로직에서 오류 발생
대칭성, 추이성, 일관성 원칙을 어겨 디버깅이 어려워짐
equals() 메서드 재정의 시 Object 명세 규약
x, y, z 는 null이 아닌 모든 참조 값
반사성 (Reflexivity) :x.equals(x)는 항상 true를 반환해야 함
대칭성 (Symmetry):x.equals(y)가 true이면y.equals(x)도 true여야 함
추이성 (Transitivity):x.equals(y)가 true이고,y.equals(z)도 true이면x.equals(z)도 true여야 함
상속보다 컴포지션 사용 권장 (아이템18)
-> Point를 상속하는 대신 private 필드로 둔다
일관성 (Consistency): equals 메서드의 결과는 객체가 변경되지 않는 한 일관되어야 함
호스트 이름 비교
먼저 두 URL의 호스트 이름(예: example.com)을 비교합니다.
IP 주소 확인 (문제 발생 지점)
호스트 이름이 같으면, URL은 각 호스트 이름에 해당하는 IP 주소를 확인하기 위해 네트워크 I/O를 수행합니다. 즉, DNS (Domain Name System) 서버에 쿼리를 보내 IP 주소를 가져옵니다.
IP 주소 비교
가져온 IP 주소가 같으면 두 URL은 같다고 판단합니다.
null - 아님:x.equals(null)은 항상 false를 반환해야 함
양질의 equals() 메서드 구현 단계별 정리
==연산자를 사용하여 자기 참조 확인 (빠른 반환)instanceof를 사용하여 타입 체크 수행비교 대상 객체를 적절한 타입으로 형변환
중요한 필드들의 값을 비교하여 논리적 동등성 확인
완성된 equals 예시
핵심 구현 팁
1. 최적화 순서
계산 비용이 저렴한 필드 먼저 비교
다를 가능성이 큰 필드
2. 불변 클래스의 캐싱 기법
3. equals를 재정의할 때는 hashCode도 반드시 함께 재정의해야 한다 (Item 11).
equals를 재정의할 때는 hashCode도 반드시 함께 재정의해야 한다 (Item 11).
4. 너무 복잡하게 해결하려 들지 않는다.
필드들의 동치성만 검사해도
equals규약을 어렵지 않게 지킬 수 있습니다.
5. 매개변수 타입은 Object 유지
@Override 애너테이션을 일관되게 사용하면 실수를 예방 할 수 있습니다.
6. AutoValue 프레임워크 사용
equals, 테스트 코드를 작성해주는 오픈소스
google이 만든 프레임 워크
결론
equals는 필요한 경우에만 신중하게 재정의해야 함.5가지 규약을 반드시 지켜야 하며, 이를 어기면 예측 불가능한 버그 발생 가능.가변 객체에서는
equals재정의를 피하는 것이 좋음.AutoValue 프레임워크 쓰자
"equals를 재정의할 때는 마치 시를 쓰는 마음으로, 모든 규약을 준수하는 동시에 객체의 본질을 정확히 반영해야 합니다." - 조슈아 블로크
Last updated