본문 바로가기
Java

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

by 신입같은 3년차 2020. 12. 16.
728x90

이번 Item10은 equals에 대한 내용에 대한 글이 있습니다


equals 메서드는 재정의하기 쉬워 보이지만 잘못된 구성을 하면 생각하지도 못한 사이드 이펙트때문에 디버깅하는데 문제를 발생시킬 수 있습니다.

문제를 발생시키지 않기 위해서는 애초에 재정의 하지 않는것을 추천드립니다만 언젠가는 구현해야할 내용입니다.

다음 내용은 어떠한 경우에 재정의하지 않아도 되는지 알아보겠습니다.

각인스턴스가 본질적으로 고유하다

인스턴스의 '논리적 동치성'을 검사할 일이 없다

상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다
대부분의 set 구현체는 AbstractSet이 구현한 equals를 상속받아 사용하고
List는 AbstractList, Map은 AbstractMap으로부터 상속받아 사용한다.

클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다.
equals가 실수로라도 호출되는걸 막고 싶다면 다음처럼 구현한다.

@Override public boolean equals(Object o) {
    throw new AssertionError();// 호출 금지 !
}

그렇다면 equals를 재정의해서 써야할 때는 언제일까??
equals가 쓰이는 때는 두객체가 물리적으로 같은가가 아니라 논리적 동치성을 확인해야 하는데 상위 클래스의 equals가 논리적 동치성을 비됴하도록 재정의되지 않았을때입니다.

EffectiveJava에서는 주로 값클래스들이 해당된다고 합니다.

값 클래스란 Integer나 String 처럼 값을 표현하는 클래스를 말한다

개발자가 실제로 객체를 equals로 비교하는것은 객체가 같은지가 아니라 객체 안에있는 값이 같은지를 궁금해하기 때문이다. 이럴때 객체안에 equals를 Override하여 해당 값을 비교하도록 재정의하면 됩니다.

추가적으로 값 클래스여도 equals를 재정의 하기 싫다면
Item1에 나온것처럼 인스턴스가 2개이상이 나오지 않도록 보장이 된다면 equals를 꼭 재정의 하지 않아도 된다. 뒷 부분에 나오겠지만 Item34 Enum 클래스도 해당된다.


equals를 재정의할때 따라야 하는 일반 규약

다음 나오는 내용은 Object 명세에 적힌 규약을 그대로 적었습니다.

  • 반사성(reflexivity) : null 이 아닌 모든 참조 값 x에 대해 , x.equals(x)는 true
    단순하게 객체는 자기 자신과 같아야 한다.

  • 대칭성(symmetry) : null이 아닌 모든 참조 값 x,y 에 대해, x.equals(y)가 true라면 y.equals(x) 도 true다.
    두 객체는 동치 여부에 똑같이 대답해야한다.

  • 추이성(transitivity) : null이 아닌 모든 참조 값x , y , z에 대해서 x.equals(y)가 true이고 y.equals(z)가 true라면 x.equals(z)도 true다.
    첫번째 객체와 두번째 객체가 같고, 두번째 객체와 세번째 객체가 같다면 첫번째와 세번째 객체도 같아야한다

  • 일관성(consistency) : null이 아닌 모든 참조 값 x,y 에 대해 x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 false 를 반환한다.
    두 객체가 같다면 수정되지 않는한 영원히 같아야한다.

  • null이 아닌 모든 참조값 x에 대해 x.equals(null)은 false이다.`


꼭 필요한 경우가 아니라면 equals를 재정의하지 말자. 혹시라도 만들어야 한다면 위의 다섯 가지 규약을 확실히 지켜가며 만들어야한다.

728x90
반응형

댓글