책/Effective Java

@Override 어노테이션 사용법

728x90
반응형
  • @Override - 메서드 선언에만 달 수 있으며, 이 어노테이션의 의미는 상위 타입(부모 타입)의 메서드를 재정의했음을 의미한다. 
  • @Override 어노테이션을 일관되게 사용한다면 여러 악명 높은 버그들을 예방할 수 있는 아주 좋은 습관이라고 말할 수 있다. 

Ex) 영어 알파벳 2개로 구성된 문자열을 표현하는 클래스 

//버그를 찾아보자 

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 bigram) {
        return bigram.first == first && bigram.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());
    }
}
  1. Set은 중복을 허용하지 않으니 26이 기대 출력은 26이지만, 실제 출력은 260이 출력된다. 
  2. equals를 재정의(override)한게 아니라 다중 정의(overloading)했다.
    • Object를 재정의하려면 매개변수 타입을 Object로 설정
    • 따라서 Object에서 상속한 equals와는 시그니쳐가 다른 또 하나의 equals를 정의한 것 
    • Object의 equals는 == 연산자와 똑같이 객체 식별성(identity)만을 확인한다. 

 

해당 오류는 컴파일러가 찾아낼 수 있지만 결국 Object.equals를 재정의한다는 의도를 개발자가 명시적으로 보여줘야한다. 

해당 코드는 재정의 의도를 명시한 코드이다.

@Override
public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
}
  • 이처럼 바꾸게 된다면 컴파일러는 컴파일 오류를 발생시켜 잘못한 부분을 명확히 알려주게 된다. 
  • 컴파일러가 잘못된 부분을 알려주고 이후에 수정한 equals 메서드이다.
@Override
public boolean equals(Object o) {
    if (!(o instanceof Bigram))
    	return false;
    Bigram b = (Bigram) o;
    return b.first == first && b.second == second;
}

 

이를 통해 우리는 @Override를 통해 쉽게 잘못된 부분을 찾을 수 있으므로
상위 클래스의 메서드를 재정의하려는 모든 메서드에는 @Override 어노테이션을 다는 습관을 갖자

예외는 오직 한 가지 

  • 구체 클래스에서 상위 클래스의 추상메서드를 재정의할 때는 굳이 @Override를 달지 않아도 된다. 
    • 구체클래스인데 아직 구현하지 않은 추상 메서드가 남아 있다면 컴파일러가 그 사실을 바로 알려주기 때문 
    • 물론 재정의 메서드 모두에 @Override를 일괄로 붙여두어도 상관없다.
  • @Override를 일관되게 사용한다면 실수로 재정의했을 때 경고해줄 것 -> 이는 재정의할 의도였으나 실수로 새로운 메서드를 추가했을 때 알려주는 컴파일 오류의 보완재 역할
  • @Override는 클래스 뿐아니라 인터페이스의 메서드를 재정의할 때도 사용할 수 있음 
    • 디폴트 메서드를 지원하기 시작하면서, 인터페이스 메서드를 구현한 메서드에도 @Override를 다는 습관을 들이면 시그니처가 올바른지 재차 확신할 수 있다. 
    • 구현하려는 인터페이스에 디폴트 메서드가 없음을 안다면 이를 구현한 메서드에서는 @Override를 생략해 코드를 조금 더 깔끔히 유지해도 무방 
    • 다만, 추상 클래스나 인터페이스에서는 상위 클래스나 상위 인터페이스의 메서드를 재정의하는 모든 메서드에 @Override를 다는 것이 좋다. -> 상위 클래스가 구체 클래스든 추상 클래스든 마찬가지 
      • Set 인터페이스는 Collection 인터페이스를 확장했지만 새로 추가한 메서드는 없다. 
      • 따라서 모든 메서드 선언에 @Override를 달아 실수로 추가한 메서드가 없음을 보장했다. 
정리 
- 재정의한 모든 메서드에 @Override 어노테이션을 의식적으로 달면 실수했을 떄 컴파일러가 알려줄 것이다. 
- 예외는 한 가지 뿐이다. 구체 클래스에서 상위 클래스의 추상 메서드를 재정의한 경우엔 어노테이션을 달지 않아도 괜찮다. 
- 그래도 우리 개발자는 의식적으로 달려고 해보자.

 

 

 

 

 

728x90
반응형