매서드를 재정의 할 때, @Ovveride 애너테이션을 붙이지 않으면 다음과 같은 실수를 할 수 있다
public class TrumpCard {
private final Symbol symbol;
private final Number number;
public TrumpCard(Symbol symbol, Number number) {
this.symbol = symbol;
this.number = number;
}
public boolean equals(TrumpCard card) {
// 심볼과 숫자가 동일하면, 같은 객체라 한다
return card.symbol == symbol && card.number == number;
}
public int hashCode() {
return 31 * symbol.hashCode() + number.hashCode();
}
public static void main(String[] args) {
Set<TrumpCard> trumpCards = new HashSet<>();
// 스페이드 카드를 A ~ K,J,Q 까지 두 번 set에 추가한다
for (Number value : Number.values()) {
trumpCards.add(new TrumpCard(Symbol.SPADE, value));
}
for (Number value : Number.values()) {
trumpCards.add(new TrumpCard(Symbol.SPADE, value));
}
System.out.println(trumpCards.size());
}
}
같은 스페이드 심볼 카드를 A ~ J,Q,K 까지 생성하기에, 원하는 출력 결과는 13
이다
하지만 실제로 실행하면 콘솔에는 26
이 나온다
이유는 Object
의 equals
를 재정의 한 것이 아닌 다중정의
해버렸기 때문이다
재정의
를 위해 @Override
애너테이션을 붙인다면 빨간 줄과 함께 오류가 뜰 것이다
@Override // 실제로는 애너테이션에 빨간 줄이 그인다!
public boolean equals(TrumpCard card) {
return card.symbol == symbol && card.number == number;
}
Method does not override method from its superclass
이는 재정의한다고 명시했는데, 정작 쓴 코드가 Object의 equals와 시그니처가 달라 다중정의 되었기 때문이다
매개변수를 Object로 수정하거나, 아니면 처음부터 인텔리제이가 만들어주는 코드를 기반으로 작성하도록 하자
@Override
public boolean equals(Object o) {
if (!(o instanceof TrumpCard))
return false;
TrumpCard card = (TrumpCard) o;
return card.symbol == symbol && card.number == number;
}
단, 구체 클래스에서 상위 클래스의 추상 메서드를 구현할 때는 달지 않아도 된다
제대로 구현하지 않은 클래스가 있으면, 컴파일 시점에서 알 수 있기 때문이다
// 오류로 컴파일되지 않는다
public abstract class Crew {
public abstract boolean isLevel1();
public abstract String helloWorld();
}
public class BackendCrew extends Crew{
public boolean isLevel1(String mission) {
if (mission.equals("blackjack"))
return true;
return false;
}
public String helloWorld() {
return "System.out.println(\"Hello World!\");\n";
}
}
인터페이스에 디폴트 매서드가 없다면, 깔끔한 코드 유지를 위해 @Override를 쓰지 않아도 좋다
하지만 디폴트 메서드가 존재한다면, @Override로 시그니처가 올바른지 재차 확인할 수 있다
public interface Crew {
default boolean isLevel1() {
return true;
}
String helloWorld();
}
// 의도가 재정인데 @Override가 없다면, TrumpCard 코드처럼 다중정의가 일어난다
public class BackendCrew implements Crew {
// 컴파일되지 않는다
@Override
public boolean isLevel1(String mission) {
if (mission.equals("blackjack"))
return true;
return false;
}
@Override
public String helloWorld() {
return "System.out.println(\"Hello World!\");\n";
}
}
재정의하는 모든 메서드에 @Override를 달면, 컴파일 시점에 실수를 고칠 수 있다
추상 메서드 구현이나, 디폴트 메서드가 없는 인터페이스 구현 때는 달지 않아도 된다
하지만 손해볼 것 없으니 인텔리제이가 시키는 대로 하자