Skip to content

item 40 dodo4513

황도영 edited this page Apr 17, 2020 · 1 revision

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

  • 자바가 기본으로 제공하는 애너테이션 중 보통의 프로그래머에게 가장 중요한 것은 @Override일 것이다. @Override는 메서드 선언에만 달 수 있으며, 이 애너테이션이 달렸다는 것은 상위 타입의 메서드를 재정의했음을 뜻한다. 이 애너테이션을 일관되게 사용하면 여러 가지 악명 높은 버그들을 예방해준다.

Bigram 이 정상동작할까?

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;
    }
    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());
    }
}
  • main 메서드를 보면 똑같은 소문자 2개로 구성된 바이그램 26개를 10번 반복해 집합에 추가한 다음, 그 집합의 크기를 출력한다. Set은 중복을 허용하지 않으니 26이 출력될 거 같지만, 실제로는 260이 출력된다. 무언가 잘못됐다!

왜???

  1. 확실히 Bigram 작성자는 equals 메서드를 재정의하려 한 것으로 보이고 hashCode도 함께 재정의해야 한다는 사실을 잊지 않았다.
  2. 그런데 안타깝게도 equals를 ‘재정의(overriding)’한 게 아니라 ‘다중정의 (overloading)’해버렸다.
  3. Object의 equals를 재정의하려면 매개변수 타입을 Object로 해야만 하는데, 그렇게 하지 않은 것이다.
  4. 그래서 Object에서 상속한 equals와는 별개인 equals를 새로 정의한 꼴이 되었다. Object의 equals 는 == 연산자와 똑같이 객체 식별성(identity)만을 확인한다.
  5. 따라서 같은 소문자를 소유한 바이그램 10개 각각이 서로 다른 객체로 인식되고, 결국 260을 출력한 것이다.

해결책은 @Override

@Override public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
}
  • 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 달자.
  • 예외는 한 가지뿐이다. 구체 클래스에서 상위 클래스의 추상 메서드를 재정의할 때는 굳이 @Override를 달지 않아도 된다. 구체 클래스인데 아직 구현하지 않은 추상 메서드가 남아 있다면 컴파일러가 그 사실을 바로 알려주기 때문이다. 물론 재정의 메서드 모두에 @Override를 일괄로 붙여두는 게 좋아 보인다면 그래도 상관없다.
Clone this wiki locally