상속 관계에 있는 클래스에서 부모 클래스의 메서드를 재정의 하는 것입니다. 오버라이딩은 다형성을 실현하는 데 핵심적인 역할을 하며, 동적 디스패치를 가능하게 합니다.
- 정의
- 메서드 오버라이딩은 서브클래스에서 부모 클래스의 메서드를 재저으이하는 것을 의미합니다.
- 오버라이딩된 메서드는 부모 클래스와 동등한 이름, 반환 타입, 매개변수를 가져야 합니다.
- 목적
- 부모 클래스의 기본 동작을 서브클래스에 맞게 변경할 수 있습니다.
- 다형성을 실현하여, 동일한 인터페이스로 다양한동작을 수행할 수 있습니다.
- 메서드 시그니처
- 오버라이딩되는 메서드는 부모 클래스의 메서드와 동일한 이름, 반환 타입, 매개변수를 가져야합니다.
- 접근 제어자
- 오버라이딩 메서드는 부모 클래스의 메서드보다 더 좁은 접근 제어자를 가질 수 없습니다. 예를 들어, 부모 클래스의 메서드가 'protected'라면 오버라이딩 메서드는 'protected'나 'public'이어야 합니다.
- 예외
- 오버라이딩 메서드는 부모 클래스의 메서드가 던질 수 있는 예외보다 더 넓은 범위의 예외를 던질 수 없습니다. 즉, 더 적은 수의 예외를 던지거나, 같은 예외를 던질 수 있습니다.
- @Override 애너테이션
- @Override 애너테이션을 사용하여 메서드가 부모 클래스의 메서드를 오버라이드하고 있음을 명시할 수 있습니다. 이는 컴파일러가 올바르게 오버라이딩되고 있는지 확인하는 데 도움이 됩니다.
- 이 애너테이션은 메서드가 부모 클래스나 인터페이스의 메서드를 오버라이드하고 있음을 컴파일러에 알리는 역할을 합니다.
이를 통해 오버라이드 의도와 실제 오버라이딩이 일치하는지 검증할 수 있습니다.
- 컴파일 타임 검증
- 컴파일러가 메서드가 실제로 무코 클래스 또는 인터페이스의 메서드를 올바르게 오버라이드하고 있는지 확인하도록 합니다.
- 만약 오버라이드하려는 메서드가 부모 클래스나 인터페이스에 존재하지 않거나, 시그니처가 일치하지 않는다면 컴파일 오류가 발생합니다.
- 오버라이드 의도 명시
- 코드를 읽는 다른 개발자에게 이 메서드가 부모클래스나 인터페이스의 메서드를 오버라이드하고 있음을 명확히 알릴 수 있습니다.
- 컴파일 타임 검증
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@java.lang.Override
public void sound() {
System.out.println("Woof");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.sound(); // Woof
}
}
- 정적 바인딩
- 컴파일 타임에 어떤 메서드가 호출될지 결정하는 방식입니다. 예를 들어, 메서드 오버로딩은 정적 바인딩입니다.
- 동적 디스패치
- 런타임에 어떤 메서드가 호출될지 결정하는 방식입니다. 메서드 오버라이딩은 동적 디스패치에 의해 작동합니다.
- 객체의 실제 타입에 따라 메서드 호출이 결정됩니다.
자바에서 동적 디스패치는 vtable을 통해 구현됩니다.
- vtable
- 각 클래스는 자신의 가상 메서드 테이블을 가집니다. 이 테이블은 메서드의 주소를 포함한 배열입니다.
- 상위 클래스의 메서드를 오버라이딩하면, 서브클래스의 vtable에 해당 메서드의 주소가 갱신됩니다.
- 메서드 호출 과정
- 객체의 실제 타입을 확인하여 vtable을 참조합니다.
- 호출할 메서드의 주소를 vtable에서 찾아 실행합니다.
- 컴파일 타임
- @Override 애너테이션을 통해 컴파일러가 메서드 시그니처를 확인하고, 올바르게 오버라이딩되었는지 검증합니다.
- 각 클래스의 vtable을 생성합니다.
- 런타임
- 객체가 생성될 때, 해당 객체는 자신의 클래스에 대응하는 vtable을 참조합니다.
- 메서드 호출 시, 객체의 실제 타입에 따라 vtable을 통해 메서드 주소를 찾아 호출합니다.
- 다형성
- 동일한 인터페이스로 다양한 객체를 처리할 수 있습니다.
- 코드의 유연성과 재사용성을 높입니다.
- 유지보수
- 부모 클래스의 기본 동작을 변경하지 않고 서브클래스에서 동작을 변경할 수 있습니다.
- 코드 변경 시 서브클래스의 영향 범위를 최소화할 수 있습니다.
- 확장성
- 새로운 서브클래스를 추가하여 기능을 확장할 수 있습니다.
- 기존 코드를 수정하지 않고도 새로운 기능을 추가할 수 있습니다.
- 성능
- 동적 디스패치로 인한 약간의 성능 오버헤드가 발생할 수 있습니다.
- 그러나 이는 대부분의 경우 무시할 수 있을 정도로 작습니다.
- 복잡성
- 상속 계층이 깊어지면, 메서드 호출의 추적이 어려워질 수 있습니다.
- 코드의 복잡성이 증가할 수 있습니다.
자바에서는 오버라이딩 시 반환 타입이 부모 클래스 메서드의 반환 타입과 다를 수 있습니다.
이를 공변 반환 타입(Covariant Return Type)이라 합니다.
서브클래스에서 오버라이딩하는 메서드의 반환 타입을 부모 클래스의 메서드 반환 타입의 서브타입으로 변경할 수 있는 기능입니다.
class Animal {
public Animal get() {
return this;
}
}
class Dog {
@Override
public Dog get() { //Animal의 서브 타입인 Dog 반환 가능
return this;
}
}
- 상속 관계 유지
- 부모 클래스의 메서드 반환 타입이 Animal일 때, 서브 클래스의 오버라이딩 메서드 반환 타입은 Animal의 서브클래스(Dog)가 될 수 있습니다.
- 컴파일 타임 체크
- 컴파일러는 오버라이딩 메서드의 반환 타입이 부모 클래스 메서드 반환 타입의 서브타입인지 확인합니다. 서브타입이 아니면 컴파일 오류가 발생합니다.
- 타입 안정성
- 반환 타입이 더 구체적이므로, 클라이언트 코드에서 형변환 없이 안전하게 사용할 수 있습니다.
- 코드 가독성
- 반환 타입이 명확하여 코드의 가독성이 향상됩니다.
- 유연한 설계
- 클래스 계층 구조에서 유연하고 재사용 가능한 메서드를 작성할 수 있습니다.
공변 반환 타입은 타입만 변경할 수 있으며 매개변수 타입은 변경할 수 없습니다. 매개변수 타입을 변경하려면 메서드 오버로딩을 사용해야 합니다.