# 40. @Override 애너테이션을 일관되게 사용하라

## 📝 아이템 40: @Override 애너테이션을 일관되게 사용하라

### 🔹 핵심 요약

✅ @Override 애너테이션은 상위 클래스의 메서드를 재정의했음을 명확히 표시\
✅ 컴파일러에게 메서드 재정의 여부를 검증하도록 도움\
✅ 실수로 잘못된 메서드 재정의를 방지하는 중요한 도구\
✅ 가능한 모든 재정의 메서드에 @Override를 사용해야 함

***

### 📚 필수 개념 정리

#### 💡 **애너테이션**

* 메타데이터로서 코드에 대한 추가 정보를 제공
* 컴파일 과정이나 실행 시점에 특정 동작을 안내
* `@ + 애너테이션 명`의 형식으로 사용

#### 🔑 **빌트인 애너테이션**

* `@Override`: 상위 클래스 또는 인터페이스의 메서드를 재정의할 때 사용
* `@Deprecated`: 더 이상 사용되지 않거나 권장되지 않는 메서드, 클래스에 사용
* `@SuppressWarnings`: 특정 컴파일 경고를 무시하도록 설정
* `@SafeVarargs`: 제네릭 가변 인자에 대한 경고를 억제
* `@FunctionalInterface`: 해당 인터페이스가 함수형 인터페이스임을 명시

#### 💡 **메소드 재정의**

* 상속 관계에 있는 클래스에서 부모 클래스의 메소드를 자식 클래스가 새로운 방식으로 구현하는 것
* 또는, 자식 클래스가 부모 클래스의 메서드를 오버라이드(재정의) 하는 것
* `equals()` 메서드를 재정의할 때는 `반사성`, `대칭성`, `추이성`, `일관성`, `null-아님`을 잘 지켜야 함(Item 10)
* `equals()` 메서드를 재정의할 때는 `hashCode()`까지 함께 재정의해야 함(Item 11)

#### 💡 **@Override란?**

* 메서드가 상위 클래스나 인터페이스의 메서드를 정확히 재정의했는지 검증하는 애너테이션
* 컴파일 시점에 오버라이딩 오류를 잡아낼 수 있는 안전장치

#### 🧐 **@Override을 사용하지 않으면?**

* 의도와 다르게 오버로딩이 될 가능성이 있음
* 컴파일러가 재정의 여부를 확인할 수 없어 런타임 오류로 이어질 수도 있음

#### ❓ **이 코드의 결과는?**

```java
public class Bigram {
    private final char first;
    private final char second;

    public Bigram(char first, char second) {
        this.first = first;
        this.second = second;
    }

     // 주의: 이 코드는 메서드 오버로딩이지 오버라이딩이 아님!
    public boolean equals(Bigram b) {
      return b.first == first && b.second == second;
    }

    // hashCode 메서드 구현 (equals와 함께 재정의)
    public int hashCode() {
      return 31 * first + second;
    }

    public static void main(String[] args) {
      Set<Bigram> s = new HashSet<>();
      for (int i = 0; i < 10; i++)
        for (char ch = 'a'; ch <= 'z'; ch++)
          s.add(new Bigram(ch, ch));
        System.out.println(s.size()); // 26(알파벳 개수)?
    }
}
```

**→ 결과는 `260`**

👉 **hashset인데 왜 중복이 되는걸까..?**

***

### 🔍 HashSet의 중복 판단 과정

1️⃣ 해시 코드 비교

* hashCode() 메서드로 객체의 해시값 계산
* 해시값이 다르면 다른 객체로 판단

2️⃣ equals() 메서드 비교

* 해시값이 같은 경우 equals() 메서드로 최종 동등성 판단
* Object의 기본 equals() 메서드는 == 연산자와 동일 (참조 동일성)

📌 **왜 260개의 객체가 생성되었을까?**

* `equals(Bigram b)`는 Object의 `equals(Object o)`를 재정의하지 않음
* HashSet은 `Object.equals()`로 중복을 판단
* Object.equals()는 참조 동일성만 비교 → 모든 객체가 다른 객체로 인식

  ```java
  // Object.equals(): 참조 동일성만 비교
  public boolean equals(Object obj) {
      return (this == obj);
  }
  ```

✨ **equals() 구현 시 고려사항**

* 매개변수는 Object로 받아야 함
* instanceof로 타입 검사
* 안전한 타입 캐스팅
* 객체의 내부 상태 비교

***

### 💡 코드 예제 및 설명

#### ✅ 메서드 재정의

```java
// 문제가 있던 코드
public class Bigram {
    private final char first;
    private final char second;

    public Bigram(char first, char second) {
        this.first = first;
        this.second = second;
    }

    // equals 메서드 오버로딩: 원래 Object 클래스의 equals 메서드를 재정의하려 했으나,
    // 파라미터가 Bigram 타입이기 때문에 오버로딩이 아닌 별도의 메서드가 됨
    public boolean equals(Bigram b) {
      return b.first == first && b.second == second;
    }

    // 해시 코드 계산: 첫 번째 문자와 두 번째 문자를 이용해 고유한 해시 값을 계산
    public int hashCode() {
      return 31 * first + second;
    }

  // main 메서드: Bigram 객체를 HashSet에 추가하고 크기를 출력
    public static void main(String[] args) {
      Set<Bigram> s = new HashSet<>();
      for (int i = 0; i < 10; i++)
        for (char ch = 'a'; ch <= 'z'; ch++)
          s.add(new Bigram(ch, ch));
        System.out.println(s.size()); // Set에 저장된 고유한 Bigram의 개수 출력
    }
}
```

```java
// @Override 애너테이션 추가 - 컴파일 에러 발생
  @Override
  public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
  }
```

![컴파일 에러](https://github.com/user-attachments/assets/d93727a9-13ad-46f4-872b-0fe2f9f7803f)

```java
// 오류 수정
// 올바른 equals 재정의 방법
public class Bigram {
    private final char first;
    private final char second;

    public Bigram(char first, char second) {
        this.first = first;
        this.second = second;
    }

    // equals 메서드 재정의: Object 클래스의 equals를 올바르게 오버라이드
    @Override
    public boolean equals(Object o) {
        // 인스턴스 타입 확인
        if (!(o instanceof Bigram))
          return false;

        // 안전한 타입 변환
        Bigram b = (Bigram) o;

        // 동등성 비교
        return b.first == first && b.second == second;
    }

    // hashCode 메서드 재정의: equals 메서드와 함께 동작하도록 hashCode도 재정의
    @Override
    public int hashCode() {
        return 31 * first + second;
    }

    public static void main(String[] args) {
      Set<Bigram> s = new HashSet<>();
      for (int i = 0; i < 10; i++)
        for (char ch = 'a'; ch <= 'z'; ch++)
          s.add(new Bigram(ch, ch));
        System.out.println(s.size()); // Set에 들어있는 고유한 Bigram 객체의 개수 출력: 26
    }
}
```

***

### 🎯 결론

📍 모든 재정의 메서드에 @Override 애너테이션 사용

📍 IDE의 자동 생성 기능 적극 활용

***

### 💭 느낀 점

💡 @Override는 단순한 애너테이션이 아니라 코드 품질을 높이는 강력한 도구

💡 메서드 재정의 시 항상 일관성과 정확성을 고려해야 한다

💡 작은 실수가 큰 버그로 이어질 수 있으므로 주의 깊은 접근 필요
