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

`문제를 회피하는 가장 쉬운 길은 아예 재정의하지 않는 것이다.` 그냥 두면 그 클래스의 인스턴스는 오직 자기 자신과 같게 된다. 그러니 다음에서 열거한 상황 중 하나에 해당한다면 재정의하지 않는 것이 최선이다.

* `각 인스턴스가 본질적으로 고유하다.` 값을 표현하는 게 아니라 동작하는 개체를 표현하는 클래스가 여기 해당한다. Thread가 좋은 예로, Object의 equals 메서드는 이러한 클래스에 딱 맞게 구현되었다.
* `인스턴스의 '논리적 동치성'을 검사할 일이 없다.`
* `상위 클래스에서 재정의한 equals가 하위 클래스에도 딱 들어맞는다.` 예컨대 대부분의 Set 구현체는 AbstractSet이 구현한 equals를 상속받아 쓰고, List 구현체들은 AbstractList를 상속받아 그대로 사용한다.
* `클래스가 private이거나 package private이고 equals 메서드를 호출할 일이 없다.`

### 그렇다면 equals를 재정의해야 할 때는 언제일까?

`객체 식별성`(두 객체가 물리적으로 같은가)이 아니라 `논리적 동치성`을 확인해야 할 때다. 두 값 객체를 equals로 비교하는 프로그래머는 객체가 같은지가 아니라 값이 같은지를 알고 싶어 할 것이다.

값 클래스라 해도 값이 같은 인스턴스가 둘 이상 만들어지지 않음을 보장하는 인스턴스 통제 클래스라면 equals를 재정의하지 않아도 된다.

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

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

이 규약을 어기면 프로그램이 이상하게 동작하거나 종료될 것이고, 원인이 되는 코드를 찾기도 굉장히 어려울 것이다.

### 위의 조건을 토대로 양질의 equals 메서드 구현 방법을 단계별로 정리해보겠다.

* \== 연산자를 사용해 입력이 자기 자신의 참조 인지 확인한다.
* instanceof 연산자로 입력이 올바른 타입 인지 확인한다. 이때의 올바른 타입은 equals가 정의된 클래스인 것이 보통이지만, 가끔은 그 클래스가 구현한 특정 인터페이스가 될 수도 있다.
* 입력을 올바른 타입으로 형변환한다. 앞서 2번에서 instanceof를 했기 때문에 이 단계는 100% 성공한다.
* 입력 객체와 자기 자신의 대응되는 '핵심' 필드들이 모두 일치하는지 하나씩 검사한다.
* 어떤 필드를 먼저 비교하느냐가 equals의 성능을 좌우하기도 한다. 최상의 성능을 바란다면 다를 가능성이 더 크거나 비교하는 비용이 싼 필드를 먼저 비교하자
* equals를 재정의할 땐 hashCode도 반드시 재정의하자(아이템11 참고)
* Object 외의 타입을 매개변수로 받는 equals 메서드는 선언하지 말자. 이 메서드는 Object.equals를 재정의한 게 아니다. 입력 타입이 Object가 아니므로 재정의가 아니라 다중정의 한것이다.

equals를 작성하고 테스트하는 일은 지루하고 이를 테스트하는 코드도 항상 뻔하다. 다행히 이 작업을 대신해줄 오픈소스가 있으니 바로 구글이 만든 `AutoValue` 프레임워크다. 클래스에 애너테이션 하나만 추가하면 AutoValue가 이 메서드들을 알아서 작성해주며, 직접 작성하는 것과 근본적으로 똑같은 코드를 만들어줄 것이다.

> 구글 AutoValue 애노테이션 예제 필요

## 정리

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://incheol-jung.gitbook.io/docs/study/effective-java/undefined-1/2020-03-20-effective-10item.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
