Dev/Java

[Effective Java : Item 40] @Override 애너테이션을 일관되게 사용하라

김세진 2024. 3. 5. 16:16
반응형

 

 

 

 

개요

 

가장 흔히 볼 수 있는 애너테이션인 @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());
	}
}

 

Bigram 클래스는 영어 알파벳 2개로 구성된 문자열을 표현하는 클래스이다.

 

이 클래스에서는 equals를 객체의 주소가 아닌 문자열을 비교하도록 재정의했으며, 그에 따라 hashCode도 재정의하였다.

 

main을 살펴보면 a부터 z 까지 순회하며 aa, bb, ... , zz 까지 26개의 문자열로 표현된 Bigram을 Set에 넣는 작업을 하는 것을 볼 수 있다. 이 때, Set는 중복을 허용하지 않으므로 26이 출력될 것 같지만 실제로는 260이 출력된다.

 

 

이는 같은 소문자를 소유한 10개의 Bigram이 서로 다 다른 것으로 인식되어 Set에 담겼다는 것이다. 이는 바로 equals 메서드가 제대로 재정의되지 않아, 문자열 비교가 아닌 Object의 equals를 사용하여 주소값 비교가 이루어졌다는 것을 뜻한다.

 

Obejct의 equals를 재정의하려면 매개변수 타입을 Object로 하여 시그니처를 맞추어야 하지만, 위에서 정의한 equals는 Bigram을 매개변수로 받으므로 Override가 아닌 Overloading을 해버린 꼴이 되었다. 

 

이같은 버그는 식별하기도 힘들 뿐더러 런타임이 되어서야 문제를 식별할 수 있기 때문에 장애로 이어질 수 있다. 

 

 


@Override 명시

 

이러한 문제를 해결하기 위해선 재정의를 하고자 하는 메서드에는 @Override를 일관적이게 선언하면 된다. 

 

위 equals 메서드에 @Override를 명시하면 다음과 같이 컴파일 에러가 발생한다.

 

 

 

재정의하려는 메서드의 시그니처가 상위 클래스와 다르다는 의미이다. 이렇게 재정의를 하겠다고 @Override를 선언했으나, 문제가 있을 경우 컴파일 타임에서 알려주기 때문에 보다 안전하게 된다. 따라서 equals 의 최종 재정의 코드는 다음과 같다.

 

@Override
public boolean equals(Object o) {
    if (!(o instanceof Bigram)) {
        return false;
    }
    Bigram b = (Bigram) o;
    return b.first == first && b.second == second;
}

 

 

 


정리

 

1. 상위 클래스의 메서드를 재정의하려는 모든 메서드에는 @Override 애너테이션을 명시하도록 하자.

2. 추상 메서드를 재정의한 경우에는 달지 않아도 되지만, 달아서 나쁠 것은 없다.

3. 추상 클래스나 인터페이스의 경우에도 재정의하는 모든 메서드에 @Override를 다는 것이 좋다.

 

 

 

 

반응형