언어/JAVA

[Effective Java] equals는 일반 규약을 지켜 재정의하라

지나가던 개발자 2020. 3. 13. 21:39
반응형

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

 

equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있어서 자칫하면 끔찍한 결과를 초래한다.

다음 상황 중 하나에 해당한다면 재정의하지 않는 것이 최선이다.

  • 각 인스턴스가 본질적으로 고유하다
  • 인스턴스의 '논리적 동치성'을 검사할 일이 없다.
  • 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다.
  • 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다.

그렇다면 equals를 재정의해야 할 때는 언제일까? 객체 식별성이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의되지 않았을 때다. 주로 값 클래스들이 여기 해당한다. 값 클래스란 Integer와 String처럼 값을 표현하는 클래스를 말한다.

 

equals 메서드를 재정의할 때는 반드시 일반 규약을 따라야 한다. 다음은 Object 명세에 적힌 규약이다.

equals 메서드는 동치관계를 구현하며, 다음을 만족한다.

  • 반사성 : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다.
  • 대칭성 : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true다.
  • 추이성 : null이 아닌 모든 참조 값 x, y, z에 대해, x.equals(y)가 ture고 y.equals(z)도 true면 x.equals(z)도 true다.
  • 일관성 : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환한다.
  • null-아님 : null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.

양질의 equals 메서드 구현 방법을 단계적으로 보자면 4가지로 볼 수 있다.

  1. == 연산자를 사용해 입력이 자기 자신의 참조인지 확인한다.
  2. instanceof 연산자로 입력이 올바른 타입인지 확인한다.
  3. 입력을 올바른 타입으로 형변환한다.
  4. 입력 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치하는지 하나씩 검사한다.

equals를 다 구현했다면 세 가지만 자문해보자. 대칭적인가? 추이성이 있는가? 일관적인가?

또한 알아두어야 할 주의사항이 있다.

  • equals를 재정의할 땐 hashCode도 반드시 재정의하자
  • 너무 복잡하게 해결하려 들지 말자.
  • Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말자.

핵심 정리

꼭 필요한 경우가 아니면 equals를 재정의하지 말자. 많은 경우에 Object의 equals가 여러분이 원하는 비교를 정확히 수행해준다. 재정의해야 할 때는 그 클래스의 핵심 필드 모두를 빠짐없이, 다섯 가지 규약을 확실히 지켜가며 비교해야 한다.

 

반응형