7-2. 오버라이딩

Hyun Jun·2022년 1월 22일
0

자바의 정석

목록 보기
27/52
post-thumbnail
post-custom-banner

오버라이딩

정의

조상 클래스로부터 상속받은 메서드 내용을 변경하는 것.

상속 받은 메서드와 이름이 같은 메서드를 자손 클래스에서 정의하면 덮어씌워짐(override).

 

조건

조상 클래스의 메서드를 자손 클래스에서 오버라이딩하기 위해서는 선언부가 동일해야함. (메서드 이름, 매개변수, 반환타입)

다만, 접근 제어자와 예외는 변경 가능한데 다음의 제약조건이 따름

  1. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경 불가.

    대부분 같은 범위의 제어자를 사용함.

    접근 제어자의 범위 (내림차순)

    publicprotected → (default) → private

  2. 조상 클래스의 메서드보다 많은 수(혹은 더 넓은 범위)의 예외 선언 불가.

    class Parent {
        void parentMethod() throws IOException, SQLException {
            ...
        }
    }
    
    class Child extends Parent {
        void parentMethod() throws Exception {
            ... // (X) 예외의 개수가 2개보다 많지 않으니 괜찮은 것 같지만, 범위로 봤을 때는 Exception이 모든 예외를 의미하는 것이므로 틀렸음
        }
        ...
    }
  3. 인스턴스 메서드 ↔ static메서드 변경 불가.

 

오버로딩 vs 오버라이딩

class Parent {
    void parentMethod() { ... }
}
class Child extends Parent {
    void parentMethod() { ... } // 오버라이딩
    void parentMethod(int i) { ... } // 오버로딩
}

 

super

super는 조상 클래스로부터 상속 받은 멤버(변수, 메서드)를 참조할 때 자손 클래스에서 사용하는 참조변수임

멤버변수와 지역변수가 같은 이름일 때 구분을 위해 this를 사용하는 것처럼 상속 받은 멤버와 고유한 멤버의 이름이 같을 때 구분을 위해 super 사용.

thissuper 모두 인스턴스 메서드에 기본적으로 내장된 지역변수로, 소속된 인스턴스의 주소 값이 저장되어 있음.

인스턴스 메서드 안에서만 사용 가능하므로 static 메서드에서는 super 사용 불가.

 

class SuperTest {
    public static void main(String[] args) {
        Child c = new Child();
        c.method();
    }
}

class Parent {
    int x = 10;
}

class Child extends Parent {
    int x = 20;
    void method() {
        System.out.println(x); // 자손 클래스(현 클래스)의 멤버변수 x의 값 20
        System.out.println(this.x); // 자손 클래스(현 클래스)의 멤버변수 x의 값 20
        System.out.println(super.x); // 조상 클래스가 상속해준 멤버변수 x의 값: 10
    }
}

 

super() - 조상 클래스의 생성자

같은 클래스 내의 다른 생성자를 호출할 때 this()를 쓰듯이, 조상 클래스의 생성자를 호출할 때는 super()를 사용함.

인스턴스를 생성하면, 생성자들의 호출 연쇄작용으로 조상 클래스의 생성자들이 순서대로 호출되고, 결국엔 최고 조상인 Object 클래스까지 올라감.

 

최고 조상인 Object 클래스를 제외한 모든 클래스의 생성자에서는 첫째 줄에 반드시

  • this()를 사용하여 같은 클래스 내의 다른 생성자를 호출하거나,

  • super()를 사용하여 조상 클래스의 생성자를 호출해주어야 함. (조상 클래스 멤버 초기화)

    자손 클래스 멤버들이 조상 클래스 멤버들을 사용할 수도 있기 때문.

    만약 작성하지 않으면 컴파일러가 자동으로 추가함. (이 때, 조상 클래스 안에 기본 생성자는 없고 다른 생성자만 있는 경우, 컴파일 에러 발생)

    class Point {
        int x, y;
        // 생성자가 없으므로 컴파일러가 기본 생성자 Point() {} 를 만들어줌.
    }
    
    class Point3D extends Point {
        int z;
        Point3D(int x, int y, int z) {
            // 이 자리에 컴파일러가 super(); 를 채워줌.
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }

    위 예시에서는 컴파일러가 Point3D 클래스의 생성자 첫 줄에 super();를 채워주고, Point 클래스에 만들어준 기본 생성자를 참조하게 되므로 정상 작동함.

    class Point {
        int x, y;
    
        Point(int x, y) {
            this.x = x;
            this.y = y;
        }
        // 생성자가 존재하므로 컴파일러가 기본 생성자 Point() {} 를 만들어 주지 않음
    }
    
    class Point3D extends Point {
        int z;
        Point3D(int x, int y, int z) {
            // 이 자리에 컴파일러가 super(); 를 채워줌.
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }

    그러나 이렇게 Point 클래스에 기본 생성자가 아닌 다른 생성자가 존재함으로써 컴파일러가 기본 생성자를 자동적으로 만들어주지 않는 환경이 되면, Point3D 클래스의 생성자 첫 줄에 생겨나는 super();에서 참조할 생성자가 없어 컴파일 에러가 발생함.

    class Point3D extends Point {
        int z;
        Point3D(int x, int y, int z) {
            // 이 자리에 컴파일러가 super(); 를 채워줌.
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }

    에러를 방지하려면 Point3D 생성자 첫 줄에 알맞은 매개변수가 담긴 super(x, y);를 적어주거나, Point 클래스에서 기본 생성자를 따로 정의해주면 됨.

profile
Back-end Engineer 👨‍💻
post-custom-banner

0개의 댓글