부모가 물려준 메서드를 자식이 제 사정에 맞게 다시 정의하는 것. 부모 타입으로 호출해도 자식이 정의한 쪽이 실행된다.

재정의의 규칙

CustomercalcPrice(int price)VIPCustomer가 다시 정의해 10%를 깎아 돌려준다. VIP만을 위한 계산기를 따로 만들 필요가 없다.

// Customer
public int calcPrice(int price) {
    bonusPoint += price * bonusRatio;
    return price;
}
 
// VIPCustomer
@Override
public int calcPrice(int price) {
    bonusPoint += price * bonusRatio;
    return price - (int)(price * saleRatio);
}

메서드 이름과 매개변수의 타입, 개수, 순서가 부모와 같아야 한다. 반환 타입은 같거나 부모 반환 타입의 하위 타입이어야 한다. 접근 제어자는 좁힐 수 없다. 부모가 public으로 열어둔 것을 자식이 private으로 닫으면 부모 자리에 자식을 넣었을 때 부를 수 없게 되어 LSP가 깨진다.

@Override는 필수가 아니지만 붙인다. 오타로 새 메서드를 만들어버리는 실수를 컴파일러가 잡아준다.

Customer vc = new VIPCustomer(10030, "나몰라", 2000);
vc.calcPrice(10000);   // 9000

가상 메서드

변수에 붙은 타입이 아니라 실제 객체가 무엇이냐가 어느 메서드를 부를지 정한다. 이렇게 동작하는 메서드를 가상 메서드라고 부른다. 이 성질이 없으면 다형성이 성립하지 않는다. 부모 타입으로 받아봐야 부모 동작만 나올 테니까.

가상 메서드 테이블

메서드의 명령 집합은 인스턴스마다 복제되지 않고 한 벌만 있다. 대신 JVM이 클래스마다 메서드 이름과 실제 주소를 짝지은 가상 메서드 테이블을 둔다. 자식이 재정의하면 그 표의 주소가 자식 메서드를 가리키고, 재정의하지 않은 showCustomerInfo()는 주소가 그대로여서 부모 메서드가 불린다. 호출할 때는 객체가 들고 있는 표를 뒤져 주소를 찾아간다.

오버로딩과의 구분

오버로딩은 같은 클래스 안에서 이름은 같고 매개변수는 다른 메서드를 여럿 두는 것이고, 어느 것을 부를지 컴파일 시점에 정해진다. 오버라이딩은 상속 관계에서 부모의 메서드를 자식이 갈아끼우는 것이고 실행 시점에 정해진다. 이름이 비슷해 자주 헷갈리지만 겹치는 데가 없다. 오버로딩의 규칙과 예제는 메서드와 오버로딩에 있다.

static 메서드

static 메서드는 오버라이딩되지 않는다. 클래스에 속하지 객체에 속하지 않으므로 변수 타입으로 결정된다. 자식에 같은 이름의 static 메서드를 두면 부모 쪽을 가리기(hiding)만 할 뿐 재정의는 되지 않는다. 같은 이유로 프록시가 static 메서드를 가로채지 못한다. 프록시의 한계

private과 final 메서드

private 메서드와 final 메서드도 가상 메서드가 아니다. 자식이 갈아끼울 길이 없으니 호출 대상이 컴파일 시점에 정해진다. 템플릿 메서드 패턴에서 부모가 정한 순서를 자식이 못 건드리게 init()close()final을 붙이는 것이 그 자리다.

재정의되지 않는 필드

필드는 아예 재정의되지 않는다. 부모와 자식이 같은 이름의 필드를 들고 있으면 참조 변수의 타입 쪽 값이 쓰인다. 부모가 i = 5, 자식이 i = 15일 때 Parents p2 = new Child();로 받으면 p2.i는 5이고 부모가 가진 printII()도 10을 출력한다. 자식이 재정의한 printI()만 15를 출력한다. 필드까지 재정의되면 부모 클래스를 만든 사람이 예상하지 못한 값이 나와버린다.

참고

원본은 오버라이딩의 조건으로 반환형이 같아야 한다고 적었다. 자바 5부터 공변 반환 타입이 허용되어, 자식이 부모 반환 타입의 하위 타입을 돌려주는 재정의도 성립한다. JLS 8.4.8.3

원본은 자바의 모든 메서드가 가상 메서드라고 적었다. private 메서드와 final 메서드는 재정의될 수 없어 호출 대상이 컴파일 시점에 확정된다. 가상 메서드 테이블을 거치는 것은 재정의될 수 있는 인스턴스 메서드뿐이다. JLS 15.12.4.4

관련

출처