@Override

import java.util.*;

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;
    }
		
		// 똑같은 소문자 2개로 구성된 바이그램 26개를 10번 바복해 집합에 추가 -> 그 집합의 크기 출력
    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());
    }
}

Set 은 중복을 허용하지 않으니 26이 출력될 것이라 기대하지만, 실제로는 260이 출력된다.

왜냐하면, equals 메서드를 재정의(overriding) 한 게 아니라, 다중 정의(overloading) 하였기 때문이다.

객체 식별성(identity)를 확인하는 Object의 equals 를 재정의하려면 매개변수 타입을 Object로 해야 하는데 그렇게 하지 않은 것이다. 따라서, 같은 소문자를 소유한 바이그램 10개 각각이 서로 다른 객체로 인식된 것이다.

@Override 애너테이션을 달아주면, 재정의한다는 의도를 명확히 나타낼 수 있을 뿐 아니라, 코드를 잘못 작성했을 경우 컴파일러가 잘못된 부분을 명확히 알려준다.

Untitled

올바르게 수정한 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를 일괄적으로 다는 게 좋아 보인다면 달아줘도 상관없다.