2010년 11월 9일 화요일

Item 8. equals를 재정의 할 떄는 일반적인 계약을 따르라.

Item 8. equals를 재정의 할 떄는 일반적인 계약을 따르라.

Posted on : 29-10-2008 | By : 기선 | In : Java

Tags: , ,

3

참조: Effective Java 2nd Edition Item 8. Obey the general contract when overriding equals

equals 메소드 재정의은 간단해 보이지만 잘못 될 여지가 많다.

equals 메소드 재정의가 필요 없는 경우

  • 클래스 특성상 각각의 객체가 유일할 때. ex) Thread
  • “논리적인 일치” 확인 기능을 제공하는지 관심 없을 때. ex) Random
  • 이미 상위 클래스에서 재정의한 equals를 재공하며, 그 로직이 현재 클래스서도 적당할 때. ex) AbstractSet, AbstractList, AbstractMap
  • 클래스가 private 또는 package-private인 경우 equals가 절대로 호출되지 않을거라는 확신이 있을 때.

equals 메소드 재정의가 필요한 경우

  • logical equality 개념이 있는 클래스
  • 보통 value class(예외, 싱글톤, Enum 타입 – Object의 equals가 곧 logical equality)
  • ex) Integer, Date

equals를 재정의할 때 따라야 하는 일반적인 계약(JavaSE6 Object 스펙)

  • Reflexive: null이 아닌 레퍼런스 값 x에 대해, x.equals(x)는 반드시 true를 반환해야 한다.
  • Symmetric: null이 아닌 레퍼런스 값 x와 y에 대해, y.equals(x)가 true를 반환 경우에 한 해서만 x.equals(y)도 true를 반환해야 한다.
  • Transitive: null이 아닌 레퍼런스 값 x, y, z에 대해, x.equals(y)가 true고 y.equals(z)가 true면 x.equals(z)도 반드시 true여야 한다.
  • Consistent: null이 나닌 레퍼런스 값 x와 y에 대해, x.equals(y)를 몇 번 호출하든지 계속해서 일관적으로 true를 반환하거나 false를 반환해야 한다.
  • null이 아닌 레퍼런스 값 x에 대해, x.equals(null)은 반드시 false를 반환한다

규칙을 어기면 다른 객체가 어떻게 동작할지 예측하기 힘들다.

고품질 equals 메소드 레서피

  • 같은 객체를 참조하는 레퍼런스가 아닌지 확인. == 사용.
  • 정당한 타입인지 확인할 떄는 instanceof 연산자를 사용,
  • 적당한 타입으로 캐스팅
  • 각각의 필드가 같은지 확인. primitive 타입은 == 사용, 레퍼런스 타입은 equals 사용
  • 메소드 작성을 마친 뒤에, symmetric, transitive, comsistent 한지 단위 테스트를 작성한다.

example

@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber == lineNumber
&& pn.prefix == prefix
&& pn.areaCode == areaCode;
}