자바 메서드 오버라이딩: 슈퍼클래스 동작 커스터마이징
이전 장에서 슈퍼클래스와 서브클래스에 대해 다뤘습니다. 클래스가 슈퍼클래스에서 메서드를 상속받으면, 그 메서드가 final로 표시되지 않은 한 오버라이드할 수 있습니다.
오버라이딩의 이점: 서브클래스 타입에 특화된 동작을 정의할 수 있어, 서브클래스가 부모 클래스 메서드를 자신의 요구사항에 맞게 구현할 수 있습니다.
객체지향 용어로 오버라이딩은 기존 메서드의 기능을 재정의하는 것을 의미합니다.
예제
예제를 살펴봅시다.
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal 참조와 객체
Animal b = new Dog(); // Animal 참조지만 Dog 객체
a.move(); // Animal 클래스의 메서드 실행
b.move(); // Dog 클래스의 메서드 실행
}
}
실행 결과
Animals can move
Dogs can walk and run
위 예제에서 b는 Animal 타입이지만 Dog 클래스의 move 메서드를 실행합니다. 이유는: 컴파일 타임에는 참조 타입을 기준으로 체크하지만, 런타임에는 JVM이 객체 타입을 파악하여 해당 객체에 속한 메서드를 실행하기 때문입니다.
따라서 위 예제에서 Animal 클래스에 move 메서드가 있으므로 컴파일은 정상적으로 됩니다. 런타임에는 해당 객체에 특화된 메서드가 실행됩니다.
다음 예제를 고려해 보세요.
예제
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
public void bark() {
System.out.println("Dogs can bark");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal 참조와 객체
Animal b = new Dog(); // Animal 참조지만 Dog 객체
a.move(); // Animal 클래스의 메서드 실행
b.move(); // Dog 클래스의 메서드 실행
b.bark(); // 컴파일 에러!
}
}
실행 결과
TestDog.java:26: error: cannot find symbol
b.bark();
^
symbol: method bark()
location: variable b of type Animal
1 error
이 프로그램은 컴파일 타임 에러를 던집니다. b의 참조 타입인 Animal에 bark라는 이름의 메서드가 없기 때문입니다.
메서드 오버라이딩 규칙
- 인수 리스트는 오버라이드되는 메서드와 정확히 같아야 함
- 반환 타입은 슈퍼클래스에서 선언된 오버라이드된 메서드의 반환 타입과 같거나 그 하위 타입이어야 함
- 접근 수준은 오버라이드된 메서드보다 더 제한적일 수 없음. 예: 슈퍼클래스 메서드가 public이면 서브클래스에서 private이나 protected로 할 수 없음
- 인스턴스 메서드는 서브클래스에 의해 상속된 경우에만 오버라이드 가능
final로 선언된 메서드는 오버라이드할 수 없음
static으로 선언된 메서드는 오버라이드할 수 없지만 재선언(re-declare)은 가능
- 상속될 수 없는 메서드는 오버라이드할 수 없음
- 인스턴스의 슈퍼클래스와 같은 패키지에 있는 서브클래스는 private이나 final로 선언되지 않은 모든 슈퍼클래스 메서드를 오버라이드할 수 있음
- 다른 패키지에 있는 서브클래스는 public이나 protected로 선언된 non-final 메서드만 오버라이드할 수 있음
- 오버라이딩 메서드는 오버라이드된 메서드가 예외를 던지는지 여부와 관계없이 언체크 예외를 던질 수 있음. 그러나 오버라이딩 메서드는 오버라이드된 메서드에서 선언된 것보다 새롭거나 더 넓은 체크 예외를 던져서는 안 됨. 더 좁거나 적은 예외는 던질 수 있음
- 생성자는 오버라이드할 수 없음
super 키워드 사용하기
오버라이드된 메서드의 슈퍼클래스 버전을 호출할 때 super 키워드를 사용합니다.
예제
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
super.move(); // 슈퍼클래스 메서드 호출
System.out.println("Dogs can walk and run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal b = new Dog(); // Animal 참조지만 Dog 객체
b.move(); // Dog 클래스의 메서드 실행
}
}
실행 결과
Animals can move
Dogs can walk and run