기본적으로 equla()는 Object 클래스에 정의되어 있어, 참조 비교(두 객체가 동일한 메모리 위치에 있는지 확인)만 수행한다.
이를 논리적으로 두 객체가 같은지를 비교하기 위해서는 equlas() 메서드를 재정의해야 한다.
equals를 재정의해야 할 때는 언제일까?
객체 식별성(object identity;두 객체가 물리적으로 같은가)이 아니라 논리적 동치성을 확인해야 할 때이다.
equlas 메서드는 동치 관계를 구현하기 위해 다음과 같은 성질을 만족해야 한다. (리스코프 치환 원칙과 관련)
x
에 대해, x.equals(x)
는 항상 true
x
와 y
에 대해, x.equals(y)
가 true
이면 y.equals(x)
도 true
이어야 한다.x
, y
, z
에 대해, x.equals(y)
가 true
이고 y.equals(z)
가 true
이면 x.equals(z)
도 true
이어야 한다.x
, y
에 대해, x.equals(y)
를 반복해서 호출하면 항상 true
또는 항상 false
를 반환해야 한다.x
에 대해, x.equals(null)
은 항상 false
여야 한다.null
과는 동등하지 않다고 판단해야 한다.자기 참조 검사
true
를 반환한다.if (o == this) return true;
타입 검사
instanceof
연산자를 사용해 입력 객체가 올바른 타입인지 확인한다.false
를 반환한다.if (!(o instanceof YourClassName)) return false;
형변환
YourClassName other = (YourClassName) o;
핵심 필드 비교
true
, 하나라도 다르면 false
를 반환한다.return this.field1.equals(other.field1) && this.field2.equals(other.field2);
public final class PhoneNumber {
private final short areaCode, prefix, lineNum;
public PhoneNumber(int areaCode, int prefix, int lineNum) {
this.areaCode = rangeCheck(areaCode, 999, "지역코드");
this.prefix = rangeCheck(prefix, 999, "프리픽스");
this.lineNum = rangeCheck(lineNum, 9999, "가입자 번호");
}
private static short rangeCheck(int val, int max, String arg) {
if (val < 0 || val > max)
throw new IllegalArgumentException(arg + ": " + val);
return (short) val;
}
@Override
public boolean equals(Object o) {
if (o == this) return true; // 1. 자기 참조 확인
if (!(o instanceof PhoneNumber)) return false; // 2. 타입 확인
PhoneNumber pn = (PhoneNumber) o; // 3. 형변환
return pn.lineNum == lineNum && pn.prefix == prefix && pn.areaCode == areaCode; // 4. 필드 비교
}
// 나머지 코드는 생략
}