문제를 회피하는 가장 쉬운 길은 아예 재정의 하지 않는 것이다
특히 아래의 상황 중 하나에 해당된다면 재정의 하지 않는 것이 최선이다
각 인스턴스가 본질적로 고유하다
인스턴스의 '논리적 동치성'을 검사할 일이 없다
상위 클래스에서 재정의한 equals가 하위 클래스에서도 딱 들어맞는다.
클래스가 private이거나 package-private이고 equlas 메서드를 호출할 일이 없다
더불어 상위 클래스의 equlas가 논리적 동치성을 검사하도록 정의되지 않아 그 역할을 못해줄 경우에 해당되는 말이다.
Integer나 String처럼 값을 표현하는 클래스
equals 메서드를 재정의할 때는 반드시 일반 규약을 따라야 한다.
equals 메서드는 동치관계를 구현하며, 다음을 만족한다.
- 반사성
- 대칭성
- 추이성
- 일관성
- null-아님
객체는 자기 자신과 같아야 한다.
두 객체는 서로에 대한 동치 여부에 똑같이 답해야 한다
A.equals(B)가 true라면 B.equals(A)도 true여야 한다.
첫 번째 객체와 두번째 객체가 같고, 두 번째 객체와 세 번째 객체가 같다면, 첫 번째 객체와 세 번째 객체도 같아야 한다
A.equals(B)가 true고
B.equals(C)가 true면
A.equals(C)도 true여야 한다.
좌표를 저장하는 Point class (equals 재정의),
이를 상속한 좌표와 색상까지 저장하는 ColorPoint class가 있을 때
ColorPoint의 equlas도 재정의하다 보면 추이성을 깨게 된다.
java.sql.Timestamp는 java.util.Date를 확장한 후 nanoseconds필드를 추가함
두 객체가 같다면 앞으로도 영원히 같아야 한다
(어느 한쪽도 수정되지 않는 한)
equlas의 판단에 신뢰할 수 없는 자원이 끼어들게 해서는 안 된다.
equals는 항시 메모리에 존재하는 객체만을 사용한 결정적 계산만 수행해야 한다.
java.net.URL의 equals는 주어진 URL과 매핑된 호스트의 IP주소를 이용해 비교한다.
공식 이름은 아니다.
모든 객체가 null과 같지 않아야 한다.
o.equals(null)이 true를 반환해서는 안된다.
o.equals(null)이 NullPointerException을 던져서도 안된다. (종종 실수로 발생한다)
// 묵시적 null 검사
@Override public boolean equals(Object o){
if(!(o instanceof MyType))
return false;
MyType mt = (MyType) o;
...
}
instanceof
연산자로 타입을 검사하게 된다.// 명시적 null 검사
@Override public boolean equals(Object o){
if(o == null)
return false;
...
}
이상의 비법에 따라 작성된 이상적인 equals 메서드의 예시
public final class PhoneNumber{
private final short areaCode, prefix, lineNum;
... 생략
@Override public boolean equals(Object o){
if (o == this)
return true;
if(!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber) o;
return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode;
}
... 생략
}
꼭 필요한 경우가 아니면 equals를 재정의하지 말자. 많은 경우에 Object의 equals가 원하는 비교를 정확히 수행해준다. 재정의해야 할 때는 그 클래스의 핵심 필드 모두를 빠짐없이, 다섯 가지 규약을 확실히 지켜가며 비교해야 한다.