페이지 불러오는 중
overwrite(덮어쓰기)는 파일이나 변수 값을 물리적으로 지우고 다시 쓰는 별개의 개념이며, Java 언어 명세(JLS)에는 등장하지 않습니다.@Override 검증, 반환형만 다른 정의에 대한 컴파일 에러 판정이 이뤄지는 계층.invokevirtual)가 일어나는 계층.
이 노트는 주로 언어 명세 계층을 기준으로 서술하고, 바인딩 시점을 설명할 때만 컴파일러/런타임 계층을 언급합니다.extends) 또는 인터페이스 구현(implements) 관계에서만 성립합니다. 아무 관계 없는 두 클래스의 동일한 메서드는 오버라이드가 아닙니다.Animal a = new Dog();
a.sound(); // 컴파일 타입은 Animal이지만, 실제 객체가 Dog이므로 Dog.sound() 실행void print(int x) { }
int print(int x) { } // 컴파일 에러: 반환형만 다른 중복 메서드class Animal {
String sound() { return "..."; }
}
class Dog extends Animal {
@Override
String sound() { return "멍멍"; } // 같은 시그니처로 재정의
}
Animal a = new Dog();
System.out.println(a.sound()); // "멍멍" — 실제 객체(Dog) 기준, 런타임 결정class Printer {
void print(int x) { System.out.println("int: " + x); }
void print(String x) { System.out.println("String: " + x); }
void print(int x, int y) { System.out.println("int,int: " + x + "," + y); }
}
Printer p = new Printer();
p.print(10); // print(int) — 컴파일 시점에 확정
p.print("hello"); // print(String)
p.print(1, 2); // print(int, int)@Override 애노테이션 — "안 써도 되는" 이유와 "그래도 써야 하는" 이유질문의 전제("안 쓰는 이유")를 바로잡으면, @Override는 선택적(optional)일 뿐이지 생략이 권장되는 것은 아닙니다. 오히려 재정의하는 모든 메서드에 항상 붙이는 것이 모범 사례입니다(Effective Java Item 40).
문법상 필수가 아닌 이유: 컴파일러는 애노테이션이 없어도 상위 타입에 동일한 시그니처의 메서드가 있으면 이를 오버라이딩으로 인식하고 동적 바인딩 대상으로 처리합니다. 즉 동작에는 애노테이션이 필요 없습니다.
그래도 붙이면 얻는 것:
메서드 이름이나 매개변수 타입에 오타가 있어 "재정의한 줄 알았지만 실제로는 새 메서드를 오버로드한" 실수를, 컴파일 에러로 즉시 검출합니다.
상위 클래스의 메서드 시그니처가 나중에 변경되어 재정의 관계가 깨지면, 컴파일러가 알려줍니다.
코드를 읽는 사람에게 "이 메서드는 상위 계약을 구현/재정의한다"는 의도를 명시합니다.
Java 6부터는 인터페이스 구현 메서드에도 @Override를 붙일 수 있습니다 (Java 5에서는 클래스 상속 재정의에만 허용).
새로 마주친 두 메서드가 오버라이드인지 오버로드인지 헷갈릴 때 다음을 순서대로 확인합니다.
@Override를 붙였을 때 컴파일되는가?
static 메서드인가?
static 메서드는 오버라이드가 아니라 메서드 하이딩(hiding). 동적 바인딩되지 않고 참조 타입 기준으로 호출됨static 메서드를 하위에서 같은 시그니처로 정의하면 오버라이드가 아닌 하이딩이 되며, 바인딩 규칙이 다릅니다.clone() 재정의에서 유용합니다.f(int)와 f(Integer), f(Object)가 함께 있을 때 컴파일러의 3단계 해석(박싱 없이 → 박싱/언박싱 허용 → 가변인자)으로 모호성이 발생할 수 있습니다.
댓글
GitHub 계정으로 의견이나 질문을 남길 수 있습니다.