peaches-book-study / effective-java

이펙티브 자바 3/E
0 stars 2 forks source link

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

Open Lainlnya opened 3 months ago

Lainlnya commented 3 months ago

Chapter : 6. 열거 타입과 애너테이션

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

Assignee : Lainlnya


🍑 서론

@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 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()); // 260
    }
}

왜 size가 260이 출력될까 ?

원래 set은 중복을 허용하지 않으므로 26이 출력되어야 한다.

equals 메서드를 재정의하려 한 것으로 보이고, hashCode 또한 함께 재정의했다. 하지만 여기서는 equals를 ‘재정의(overriding)’한 게 아니라 ‘다중정의(overloading)’했다.

public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    return (anObject instanceof String aString)
            && (!COMPACT_STRINGS || this.coder == aString.coder)
            && StringLatin1.equals(value, aString.value);
}

👆 자바 공식 문서에 나와있는 equals

즉, 매개변수의 타입이 Object가 아니기 때문에, overriding이 아닌 overloading이 되어, equlas를 새로 정의한 형태가 되었다. 따라서 == 만 확인했기 때문에 서로 다른 객체로 인식하여, 260을 출력하게 되었다.

여기서 컴파일러가 오류를 찾아내게 하려면, 아래처럼 재정의한다는 의도를 명시해야 한다.

@Override
public boolean equals(Bigram b) {
    return b.first == first && b.second == second;
}

// Bigram. java: 10: method does not override or implement a method 
// from a supertype
// 이라는 오류 발생
@Override
public boolean equals(Object o) {
    if (!(o instanceof Bigram))
        return false;
    Bigram b = (Bigram) o;
    return b.first == first && b.second == second;
}

⭐️ 상위 클래스의 메서드를 재정의하려는 모든 메서드에 @Override 애너테이션을 달자

예외: 구체 클래스에서 상위 클래스의 추상 클래스를 재정의할 때는 달지 않아도 된다.

✅ 구체 클래스(concrete class)란? 모든 연산에 대한 구현을 가지고 있는 클래스를 의미하며, 추상 클래스가 아닌 클래스를 모두 concrete class라고 한다. new 키워드로 객체를 생성할 수 있는 클래스이며 클래스의 모든 메서드를 완벽하게 구현한 클래스를 의미한다.

구체 클래스인데 아직 구현하지 않은 추상 메서드가 남아 있다면 컴파일러가 그 사실을 알려준다.

올바른 사용법

  1. IDE에서 관련 설정을 활성화해놓으면 @Override가 달려있지 않은 메서드가 실제로는 재정의를 했다면 경고를 준다.
  2. @Override 는 클래스뿐 아니라 인터페이스의 메서드를 재정의할 때도 사용할 수 있다. (자바에서 인터페이스에서 디폴트 메서드를 지원하기 시작했기 때문에, 디폴트 메서드가 없음을 안다면 override를 생략해 적는 것이 좋다.)
  3. 추상 클래스나 인터페이스에서는 상위 클래스나 상위 인터페이스의 메서드를 재정의하는 모든 메서드에 @Override 를 다는 것이 좋다. 예를 들어 Set 인터페이스의 경우 Collection 인터페이스를 확장했지만 새로 추가한 메서드는 없기에 모두 @Override 를 달아 실수로 추가한 메서드가 없음을 보장했다.

🍑 결론

재정의한 모든 메서드에 @Override 애너테이션을 의식적으로 달면 실수했을 때 컴파일러가 바로 알려준다.

구체 클래스에서 상위 클래스의 추상 메서드를 재정의한 경우엔 해당 애너테이션을 달지 않아도 된다.


Referenced by

youngkimi commented 3 months ago

잘봤습니다 ^^

byunghyunkim0 commented 3 months ago

interface aa { public void test(); }

public class Text implements aa{ public void test() {

}
public static void main(String[] args) {
}

} 오버라이드 안해도 경고는 발생하지 않는다.