equals 메서드는 재정의하기 쉬워 보이지만 곳곳에 함정이 도사리고 있어 끔찍한 결과를 초래할 수 있다
문제를 회피하는 가장 쉬운길은 아예 재정의 하지 않는 것이다 (4가지 방법)
1. 각 인스턴스가 본질적으로 고유하다
- 값을 표현하는게 아니라 동작하는 개체를 표현하는 클래스가 여기에 해당한다
- ex) Thread 좋은 예로, Object의 equals 메서드는 이러한 클래스에 딱 맞게 구현돼 있다
2. 인스턴스의 '논리적 동치성' 을 검사할 일이 없다
- java.util.regex.Pattern은 equals를 재정의해서 두 Pattern의 인스턴스가 같은 정규 표현식을 나타내는지를 검사하는,
즉 논리적 동치성을 검사하는 방법도 있다
- 하지만 설계자는 클라이언트가 이 방식을 원하지 않거나 애초에 필요하지 않다고 판단할 수 있다
- 설계자가 후자로 판단했다면 Object 의 기본 equals만으로 해결된다
3. 상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다
- 대부분 Set 구현체는 AbstractSet이 구현한 equals를 상속받아 쓴다
- List 구현체들은 AbstractList로부터, Map 구현체들은 AbstractMap으로부터 상속받아 그래도 쓴다
4. 클래스가 private이거나 package-private이고 equals 메서드를 호출할 일이 없다
- equals가 실수로라도 호출되는 걸 막고 싶다면 다음처럼 구현하자
@Override public boolean equals(Object o) {
throw new AssertionError(); // 호출금지!
}
그렇다면 equals를 재정의해야 할 때는 언제 일까?
- 객체 식별성이 아니라 논리적 동치성을 확인해야 하는데, 상위 클래스의 equals가 논리적 동치성을 비교하도록 재정의 되지 않을 때다
- 주로 값 클래스가 여기에 해당한다 (Integer , String)
- 두 값 객체를 equals로 비교하는 프로그래머는 값은 같은지를 알고 싶어 한다
- equals가 논리적 동치성을 확인하도록 재정의 해두면, 그 인스턴스는 값을 비교하길 원하는 프로그래머의 기대에 부응
Map의 키와 Set의 원소로 사용할 수 있게 된다
equals 메서드를 재정의할 때는 반드시 일반 규약을 따라야 한다
1. 반사성 : null이 아닌 모든 참조 값 x에 대해, x.equals(x)는 true다.
2. 대칭성 : null이 아닌 모든 참조 값 x, y에 대해, x.equals(y)가 true면 y.equals(x)도 true다.
3. 추이성 : null이 아닌 모든 참조 값 x, y, z에 대해 x.equals(y)가 true이고 y.equals(z)도 true면 x.equals(z)도 true다.
4. 일관성 : null이 아닌 모든 참조 값 x, y에 대해 x.equals(y)를 반복해서 호출하면 항상 true를 반환하거나 항상 false를 반환한다.
5.null-아님 : null이 아닌 모든 참조 값 x에 대해, x.equals(null)은 false다.
- 이규약을 어기면 프로그램이 이상하게 동작하거나 종료될 것이고, 원인이 되는 코드를 찾기도 굉장히 힘들다
Object 명세에서 말하는 동치 관계란 무엇일까?
- 집합을 서로 같은 원소들로 이뤄진 부분집합으로 나누는 연산
- 이 부분집합을 동치류(동치 클래스)라 한다.
'Books > Effective-Java 3판' 카테고리의 다른 글
10.equals는 일반 규약을 지켜 재정의하라(3) (0) | 2021.10.01 |
---|---|
10.equals는 일반 규약을 지켜 재정의하라(2) (0) | 2021.09.29 |
9.try-finally보다는 try-with-resources를 사용하라 (0) | 2021.09.27 |
8.finalizer와 cleaner 사용을 피하라 (0) | 2021.09.25 |
7.다 쓴 객체 참조를 해제하라 (0) | 2021.09.24 |