[Effective Java 3/e] 아이템 10 - equals는 일반 규약을 지켜 재정의하라
아이템 10 - equals는 일반 규약을 지켜 재정의하라
아래 상황 중 하나에 해당한다면 재정의하지 않는 것이 최선이다
- 각 인스턴스가 본질적으로 고유하다.
- 인스턴스의 ‘논리적 동치성(logical equality)’을 검사할 일이 없다.
- 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다.
- ex) Set 구현체 AbstractSet이 구현한 equals 상속받아 사용, List 구현체는 AbstractList 상속, Map 구현체는 AbstractMap 상속
- 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다.
실수로라도 equals 호출 되는것을 막는 방법
@Override public boolean equals(Object o) {
throw new AssertionError(); //호출 금지
}
equals 재정의 할 때
- 논리적 동치성을 확인해야 할 때
- 값 클래스들이 해당
equals 의 일반 규약
- 반사성(reflexivity) : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true
- 자기 자신과 같아야 함
- 대칭성(symmetry) : null이 아닌 모든 참조 값 x,y에 대해, x.equals(y)가 true 이면 y.equals(x)도 true
- 두 객체는 서로에 대한 동치 여부에 똑같이 답해야 함
- 추이성(transitivity) : null이 아닌 모든 참조 값 x,y,z에 대해 x.equals(y)가 true 이고 y.eqauls(z)도 true 이면 x.eqauls(z)도 true
- 첫 번째 객체와 두 번쨰 객체가 같고, 두 번째 객체와 세 번째 객체가 같다면, 첫 번째 객체와 세 번쨰 객체도 같아야 함
- 일관성(consistency) : null이 아닌 모든 참조 값 x,y에 대해 x.equals(y)를 반복 호출하면 항상 true 이거나 false 를 반환
- 두 객체가 같다면 영원히 같아야 함
- null 아님 : null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false
- 모든 객체가 null과 같지 않아야 함
equals 단계별 구현 방법
- == 연사자를 사용해 입력이 자기 자신의 참조인지 확인한다.
- instanceof 연산자로 입력이 올바른 타입인지 확인한다.
- 입력을 올바른 타입으로 형변환 한다.
- 입력 객체와 자기 자신의 대응되는 ‘핵심’ 필드들이 모두 일치하는지 하나씩 검사한다.
전형적인 equals 메서드의 예
public final class PhoneNumber {
private final int areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNum = 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 재정의 시 주의사항
- equals 재정의 시 hashCode도 반드시 재정의
- 너무 복잡하게 해결하려 하지 말자.
- 필드들의 동치성만 검사해도 됨
- Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말자.
// 잘못된 예- 입력 타입은 반드시 Object여야 한다.
public boolean equals(MyClass o) {
...
}