Java

Java의 상속과 생성자

딱구킴 2022. 10. 21. 18:55

최근에 면접을 보면서 내 공부 상태(?)에 충격을 먹고 자바 공부를 좀 더 깊게 해보기로 결심했습니다. 

 

최근 백기선 선생님의 자바 강의를 듣고있는데, "이거 모르시면 자바 공부 제대로 안하신거에요"라는 말에 뼈를 많이 맞아서, 뼈가 거의 으스러져서 사라지고 있습니다. (ㅠㅠ)

 

오늘도 강의를 듣다가, 상속에 대해 제대로 알고있지 않은 부분이 있어 이에 대해 정리 해보고자 합니다.

 

상속이란?

개념만 간단히 짚고 넘어가겠습니다.

 

상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것입니다. 

 

구현은 아주 간단한데, 새로 작성할 클래스 이름 뒤에, 상속 받고자 하는 클래스를 extends라는 키워드와 함께 작성해주기만 하면 됩니다.

/**
* Child : 자식 클래스, 하위(sub) 클래스
* Parent : 부모 클래스, 상위(super) 클래스
*/
class Child extends Parent() {}

 

클래스를 상속하게 되면, 상속 받은 클래스는 다음과 같은 특징을 가집니다.

  • 부모 클래스의 모든 멤버를 상속받습니다. 즉, 자식 클래스는 조상 클래스를 포함하게 됩니다.
    • 부모 클래스가 변경되면, 자식 클래스는 자동적으로 영향을 받습니다.
    • 자식 클래스가 변경되는 것은 부모 클래스에 영향을 주지 않습니다.
  • 부모 클래스의 생성자는 자식 클래스에게 상속되지 않습니다.

 

여기서, 제가 제대로 알고 넘어가지 않았던 부분이 있었습니다. 

부모 클래스의 생성자는 자식 클래스에게 상속되지 않는데, 어떻게 자식 클래스가 부모 클래스의 필드를 사용할 수 있는걸까요?

 

결론부터 말씀드리자면, 자식 클래스를 생성할 때 부모 클래스도 함께 생성되기 때문입니다.

해당 과정을 다음의 방법으로 알아보았습니다.

 

상속과 생성자

부모 클래스로부터 상속받은 메서드나 필드는 부모 클래스가 소유한 것입니다. 그렇기 때문에, 이를 사용하려면 부모 클래스가 생성되어야 합니다. 하지만, 우리는 부모클래스를 생성하지 않고 자식 클래스만 생성해도 부모 클래스의 메서드나 필드를 사용할 수 있습니다. 이게 어떻게 가능한건지 코드로 알아보았습니다.

// 부모 클래스 입니다.
public class Parent {

    public final String FIRST_NAME = "Kim";
    public static final String TYPE = "Human";

    public Parent() {
        System.out.println("부모 클래스가 생성되었습니다.");
    }

    public String getFIRST_NAME() {
        return FIRST_NAME;
    }
}


// 자식 클래스 입니다.
public class Child extends Parent {

    public void cry() {
        System.out.println("엄마 나 이거 사줘!!!!!!!");
    }

    public Child() {
        System.out.println("자식 클래스가 생성되었습니다.");
    }
}


// 실행 클래스 입니다.
public class Main {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

간단한 부모 클래스와 자식 클래스를 만들었습니다. 그리고 실행 클래스에서 자식 클래스를 생성해주고, 해당 라인에 디버그 포인트를 걸어 디버깅을 통해 확인해보았습니다.

 

디버그 포인트는 실행 클래스의 3번 라인에 걸었습니다.

 

그 결과, Child() 생성자가 Parent() 생성자를 호출하는 것을 확인할 수 있었습니다. Parent() 생성자가 Parent 인스턴스를 만들면서, 인스턴스의 변수도 초기화 하는 것을 확인할 수 있었습니다.

 

즉, 자식 클래스를 생성할 때 자식 클래스의 생성자가 부모 클래스의 기본 생성자를 호출하고 부모 클래스를 생성하기 때문에, 자식 클래스는 부모 클래스의 필드와 메서드를 사용할 수 있게 되는 것이었습니다.

 

여기에는 약간의(?) 규칙이 있습니다.

  • 자식 클래스에서 부모 클래스의 생성자를 "명시적으로 호출하지 않은"경우에만 기본 생성자를 자동으로 호출해줍니다.
  • 자식 클래스에서 부모 클래스의 생성자를 "명시적으로 호출"할 경우, 기본 생성자를 호출하지 않을 수 있습니다.

즉, 아래의 예시에서는 자식 클래스가 부모 클래스의 기본 생성자를 호출하지 않습니다.

// 부모 클래스
public class Parent {

    public final String FIRST_NAME = "Kim";
    public static final String TYPE = "Human";

    public String str;

    public Parent() {
        System.out.println("부모 클래스가 생성되었습니다.");
    }

    public Parent(String str) {
        this.str = str;
        System.out.println("Argument가 있는 부모 생성자가 호출되었습니다.");
    }

    public String getFIRST_NAME() {
        return FIRST_NAME;
    }
}

// 자식 클래스
public class Child extends Parent {

    public void cry() {
        System.out.println("엄마 나 이거 사줘!!!!!!!");
    }

    public Child() {
        super("엄마 아빠 사랑해요!!!!");  // 조상 클래스의 생성자를 명시적으로 호출
        System.out.println("자식 클래스가 생성되었습니다.");
    }
}

 

 

이러한 특징을 이용한다면, 상속이 불가능한 객체도 만들 수 있습니다.

  • 자식 객체의 생성자는 필수로 부모 객체의 생성자를 호출합니다. 때문에, 부모 객체의 생성자를 private라는 접근 제어자로 막아둔다면, 이 부모 객체는 상속이 불가능한 객체가 될 수밖에 없습니다.
public class SamChon {

    private SamChon() {
    }  // 기본 생성자의 접근을 막는다.
}

 

SamChon 클래스의 기본 생성자를 private라는 접근 제어자로 생성을 제한한 후, Me 클래스에서 상속을 시도했습니다.

그 결과, SamChon 클래스에 기본 생성자가 없기 때문에 상속을 할 수 없다고 합니다.


 

자주 사용하는 기능임에도 불구하고, 상속의 기본적인 메커니즘조차 모르고 있던 것을 반성하게 되는 시간이었습니다.

앞으로도 백기선 선생님께 자주 뼈를 맞게 될 것 같습니다. 그 때마다, 반성을 좀 하고, 기본적인 메커니즘에 대해 분석하고 블로그 글을 남겨야겠습니다.

 

 

혹시나 틀린 부분이 있다면 댓글로 알려주시면 감사드리겠습니다.

 

 

출처

https://stackoverflow.com/questions/70684085/where-is-definition-of-method-java-lang-object-initv

https://osy0907.tistory.com/m/56

https://www.inflearn.com/course/the-java-code-manipulation/unit/23432