NMP-Study / EffectiveJava2022

Effective Java Study 2022
5 stars 0 forks source link

아이템 10. equals는 일반 규약을 지켜 재정의하라 #10

Closed okhee closed 2 years ago

okhee commented 2 years ago

결론

(웬만하면) 재정의하지 않는다


다음에 해당하는 경우 재정의 금지

다음에 해당할 때 재정의 고려

equals() 재정의 시 따라야 하는 5가지 규칙

x, y, z는 non-null이라고 가정

다음 명제, 식은 모두 true여야 한다.

  1. x = x
  2. (x = y) -> (y = x)
  3. (x = y) and (y = z) -> (x = z)
  4. (x = y) -> (x = y) (시간이 지나도)
  5. x != null

순서대로 반사성(Reflexivity), 대칭성(symmetry), 추이성(transitivity), 일관성(consistency), null-아님 규칙이다.

equals() 규약 굳이 어겨보기

  1. x = x 어기기가 더 어렵다.
  2. (x = y) -> (y = x) CaseInsensitiveString > equals(Object o)에서 굳이 o가 String인 경우까지 고려하기
  3. (x = y) and (y = z) -> (x = z) 좌표 정보 Point 클래스와 이를 상속해 색깔 정보를 추가한 ColorPoint 대칭성 보장을 위해 ColorPoint > equals()에서 Point인 case에서 분기 이 경우 추이성이 깨짐

    getClass()를 이용하는 방법은 ColorPointPoint로 활용될 수 없게 만듦

  4. (x = y) -> (x = y) (시간이 지나도) equals() 판단에 불확실하거나 신뢰할 수 없는 자원이 간섭하면 안 된다.
  5. x != null x.equals(null)이 절대 true일 수 없다. x instanceof clazz에서 x가 null이면 false를 반환하는 점을 이용

올바른 equals() 재정의 방법

  1. if (this == o) return true
  2. instanceof로 입력이 올바른 타입인지 확인 올바른 타입: 현재 클래스, 또는 상위 인터페이스
  3. 입력을 올바른 타입으로 형 변환
  4. 입력 객체와 자기 자신의 '핵심 필드'들을 비교

    • float와 double을 제외한 기본 타입(primitive type)은 == 연산자로 비교
    • float와 double은 Float.compare, Double.compare로 비교(부동 소수점 등)
    • 참조 타입 필드는 각각의 equals로 비교
      
      public final class PhoneNumber {
      private final short areaCode, prefix, lineNum;

    @Override public boolean equals(Object o) { if(o == this) { return true; }

    // 추가적으로 o가 null 인지 확인 불필요
    if(!(o instanceof PhoneNumber)) {
        return false;
    }
    
    PhoneNumber pn = (PhoneNumber)o;
    return pn.lineNum == lineNum && pn.prefix == prefix
                    && pn.areaCode == areaCode;

    } }

마지막 주의 사항