Override 어노테이션을 일관되게 사용하자

2021-05-28

@Override

  • @Override는 메서드 선언에만 달 수 있다.
  • @Override가 달린 메서드는 부모의 메서드를 재정의했음을 뜻한다.

발생할 수 있는 오류

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 equlas(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());

    }
}

위의 코드는 간단히 두개 의 문자를 받고, 알파벳 a~z 까지 10번의 for문을 돌며 Set에 저장하는 간단한 코드다.

Set은 중복을 허용하지 않고, Bigram은 eqauls()와 hashcode()가 재정의 되어 있기 때문에, 만약 같은 알파벳이 들어온다면, Set에 추가되지 않는다고 예상할 수 있다.

그래서 예상되는 결과는 a~z 순회 결과인 26이 나올것이라고 예상할 수 있지만, 결과는 260이 나온다.

이 코드를 작성한 개발자는 equlas를 재정의 했다고 생각하지만, 사실은 오버라이드가 아닌 오버로딩이 되었다.

우리가 오버라이드 해야할 Object.eqauls()는 이렇게 생겼다.

Object.equlas()

public boolean equals(Object obj) {
  return (this == obj);
}

잘 살펴보면 파라미터가 Object인걸 볼 수 있다.

이런 오류를 방지하기

이런 오류를 방지하려면 내가 오버라이드 할 메서드에는 항상 @Override 어노테이션을 붙여주는 것이 좋다.

그렇게 하면, 컴파일러한테 재정의한다고 알릴 수 있기 때문에, 만약 부모의 메서드를 재정의 하지 않았다면, 컴파일러가 에러를 내준다.

한가지 예외는 있는데, 추상 클래스의 추상 메서드를 재정의할 때는 굳이 @Override 를 붙여주지 않아도 된다. 구체 클래스인데 아직 구현하지 않은 추상 메서드가 남아 있으면 컴파일러가 에러를 내주기 때문이다.

@Override 는 클래스뿐 아니라 인터페이스의 메서드를 재정의할 때 사용할 수 있다. JDK8부터 디폴트 메서드를 지원하기 시작하게 되어서, 인터페이스의 구현체에도 @Override 를 다는 습관을 들이면 시그니처가 올바른지 한번 더 확인을 할 수 있다.

그러나 구현하려고 하는 인터페이스에 디폴트메서드가 하나도 존재하지 않으면, @Override 를 생략하면 코드가 좀 더 깔끔해지긴 한다. 이 경우 생략해도 되는 이유가, 디폴트 메서드와 구현해야할 메서드의 시그니처를 올바른지 검증을 안해도 되기 때문이다.

하지만 추상 클래스나 인터페이스에서는 상위 클래스나 상위 인터페이스의 메서드를 재정의하는 모든 메서드에 @Override 를 다는 습관을 들이자.