[JAVA] Override, Overriding
자바 오버라이딩의 개념, 조건, 그리고 @Override 어노테이션이 필요한 이유를 정리한 글
Inheritance > Override
객체 지향 프로그래밍에서 다형성(Polymorphism)과 코드의 확장성을 구현하는 핵심 원리가 바로 오버라이드(Override)입니다. 이 글에서는 오버라이드의 개념과 조건, 그리고 @Override 어노테이션이 왜 중요한지 정리합니다.
오버라이드의 개념과 필요성
오버라이드란?
오버라이드는 슈퍼클래스에 정의된 메서드를 서브클래스에서 재정의하거나, 슈퍼인터페이스에 정의된 메서드를 서브 클래스에서 재정의하는 것을 의미합니다.
오버라이드의 필요성
오버라이드는 객체 지향 프로그래밍에서 다형성과 코드의 확장성을 구현하는 핵심 원리입니다. 오버라이드가 없다면 다형성 구현에 제약이 생깁니다.
자동차를 예로 들어 보면 다음과 같습니다.
// 부모 클래스: Vehicle
class Vehicle {
void move() {
System.out.println("탈것이 움직입니다.");
}
}
class Car extends Vehicle {
@Override
void move() {
System.out.println("자동차는 네 바퀴로 달립니다.");
}
}
Vehicle은 탈것이 움직인다는 공통 개념을 제공하고, 자식 클래스 Car는 move()를 재정의해서 자동차만의 동작을 표현합니다.
오버라이드의 조건
클래스에서 오버라이딩
- 서브클래스 C는 슈퍼클래스 A를 상속받아야 합니다.
- 슈퍼클래스 A의
mA를 상속받아 만들어진 서브클래스 C의mC는 새로운 구현이어야 합니다. - 시그니처 호환성을 만족해야 합니다.
- 메서드 이름이 같아야 합니다.
- 매개변수 목록(개수, 순서, 타입)이 같아야 합니다.
- 정확히는
mC의 시그니처가mA의 서브 시그니처여야 합니다. - 반환 타입은 상위 반환 타입과 같거나 하위 타입이어야 합니다. (Java SE 5.0부터)
- 접근 제한자는 상위 메서드보다 더 넓거나 같아야 합니다.
- 오버라이딩 대상이
static메서드인 경우 기대한 다형성이 동작하지 않습니다. final메서드는 오버라이딩할 수 없습니다.private메서드는 서브클래스에 상속되지 않습니다.- 조상보다 더 큰 예외를 던질 수 없습니다.
- 리턴 타입은 호환되어야 합니다.
인터페이스에서 오버라이딩
- 디폴트 메서드는 클래스에서 필요에 따라 선택적으로 오버라이드할 수 있습니다.
- 클래스에 상속된 구체적인 메서드는 인터페이스에 선언된 추상 메서드를 오버라이드합니다.
- 인터페이스는
private또는static메서드를 슈퍼 인터페이스로부터 상속하지 않습니다. - 오버라이드된 디폴트 메서드는 슈퍼인터페이스 이름으로 한정된
super키워드를 사용해 접근할 수 있습니다. - 서로 다른 디폴트 메서드가 같은 시그니처로 상속되면 컴파일 에러가 발생할 수 있습니다.
@Override 어노테이션의 역할과 중요성
슈퍼클래스 A를 상속한 서브클래스 C가 mA를 재정의해 mC를 만들었다면 자동으로 오버라이딩이 될 텐데, 왜 굳이 @Override를 붙일까요?
- 컴파일 타임 오류 방지
- 컴파일러에게 상위 클래스 또는 인터페이스의 메서드를 오버라이드하는 것임을 알려 줍니다.
- 오버라이딩 규칙에서 벗어나면 컴파일 오류를 발생시켜 실수를 빠르게 발견할 수 있습니다.
- 코드 가독성 및 유지보수성 증가
- 다른 개발자가 코드를 읽을 때 오버라이딩된 메서드라는 점을 즉시 이해할 수 있습니다.
public class Vehicle {
public void move(int velocity) {
System.out.println("탈 것이 " + velocity + "의 속도로 움직이고 있습니다.");
}
}
public class Car extends Vehicle {
public void move(double velocity) {
System.out.println("자동차가 " + velocity + "의 속도로 움직이고 있습니다.");
}
}
public class Solution {
public static void main(String args[]) {
Vehicle vehicle = new Vehicle();
vehicle.move(1);
Vehicle carNoAnnotation = new Car();
carNoAnnotation.move(1);
Car car = new Car();
car.move(1.22);
}
}
위 코드는 어노테이션 없이 작성된 예시입니다. 매개변수 타입이 달라졌기 때문에 오버라이딩이 아니라 오버로딩이 일어나도 개발자가 바로 알아차리기 어렵습니다.

이처럼 move의 매개변수를 바꿔도 곧바로 오류가 드러나지 않을 수 있습니다. 그래서 오버라이딩을 의도했다면 @Override를 붙여 컴파일러가 검증하도록 만드는 것이 안전합니다.

어노테이션을 적용하면 컴파일러가 규칙 위반을 더 명확하게 잡아 줍니다.