본문 바로가기

이펙티브 자바

equals는 일반 규약을 지켜 재정의하라

모든 클래스는 Object 클래스를 상속하고 있다. Object 클래스를 상속 함으로 Object 가 제공하는 메서드를 사용하거나 재정의(overriding)할 수 있다.Object가 제공하는 메서드의 예로는 equals, hashcode, clone, toString, finalize 가 있다. 이 중에서 equals는 객체, 값과 비교할 때 쓰인다. equals를 재정의(overriding)을 하지 않으면 객체를 비교할 때는 주소 즉 물리적 동치성(==)을 비교하게 된다. 쉽게 말하자면 객체가 가지고 있는 필드 값이 같더라도 똑같은 객체가 아니기 때문에 물리적 동치성은 같지 않다.

public class AA {
    private final String a;

    public AA(String a) {
        this.a = a;
    }
}

위의 코드는 AA라는 클래스에 String 형태에 a라는 변수가 있다. equals 메서드를 재정의 하지 않고 Object가 제공하는 메서드를 사용하고 있다.

AA aa = new AA("aaa");
AA bb = new AA("aaa");

System.out.println(aa.equals(bb));

코드 결과는 false가 출력이 된다. 이유는 간단하다. Object가 제공하는 equals는 물리적 동치성을 비교하기 때문이다. 논리적 통치성 기준으로 비교하고 싶다면 밑에 코드처럼 바꾸면 된다.

public class AA {
    private final String a;

    public AA(String a) {
        this.a = a;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AA aa = (AA) o;
        return Objects.equals(a, aa.a);
    }

}

의문점이 하나 생길 것이다. 어떤 경우에 object가 제공하는 equals를 사용하고 어떤 경우에는 equals를 재정의 해야 할까?
equals를 재정의 하면 안 되는 상황은 4가지 상황이 있다.

  • 각 인스턴스가 본질적으로 고유하다. (값을 비교하는게 목적이 아닌 동작하는 객체로 표현)
  • 논리적 동치성을 비교하고 싶지 않은 경우
  • 상위 클래스에서 재정의한 equals가 잘 맞는 경우
  • equals 메서드 호출할 일이 없는 경우
    위의 경우가 아닐 때 equals를 재정의 해서 사용하면 된다. 재정의 할 때도 주의점이 5가지 있다.
  • 반사성
    반사성이란 x가 null이 아닐 때 x.equals(x)는 항상 true 여야 한다. equals의 본질을 일부로 깨지 않는 이상 반사성은 거의 성립한다.
  • 대칭성
    대칭성이란 x,y가 null이 아닐 때 x.equals(y)가 true이면 y.equals(x)도 true여야 한다.(equals를 재정의 할 때 동일 객체 선까지만 재정의 해야 한다.)
  • 추이성
    추이성이란 x,y,z가 null이 아닐때 x.equals(y)가 true y.equals(z)가 true이면 z.equals(x)도 true여야 한다.
  • 일관성
    x.equals(y)가 true이면 계속 true여야 한다.
  • null아님
    null이 아닌 모든 참조 값 x에대해 x.equals(null)은 false이다