ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] Inheritance, Overriding
    Programming Lang/Java 2022. 9. 9. 02:02

    상속이란?

    inheritance (상속)
    a physical or mental characteristic inherited from your parents, or the process by which this happens
    부모로부터 물려받은 신체적 또는 정신적 특성, 또는 이것이 일어나는 과정
    overriding (오버라이딩)
    to take control over something, especially in order to change the way it operates
    특히 운영 방식을 바꾸기 위해 무언가를 통제하는 것

    a device that changes the control of a machine or system in special situations, especially from automatic to manual
    자동에서 수동으로 특별한 상황에서 기계나 시스템의 제어를 바꾸는 장치

     

    상속의 목적, 이용 방법

    조상클래스와 자손클래스와의 관계는 조상클래스를 자손클래스가 포함하는 관계.

    • 기존 클래스를 재사용하여 새로운 클래스 작성
    • 코드를 변경
    • extends 사용

     

    상속의 장점, 특징

    1. 중복 최소화

    • 코드 재사용성 높임
    • 관리 및 변경 쉬움. 유지보수 편리
    • 일관성 유지
    • 반복 작업 최소화


    2. 기능 확장

    • 자손클래스의 변경은 조상클래스에 아무런 영향도 주지 못한다.
    • 자손클래스 멤버 갯수 >= 조상클래스 멤버 갯수
    • 생성자와 초기화 블럭은 상속되지 않는다. (멤버, 즉 변수/메서드/클래스만 상속)


    3. 단일 상속만 가능

    • 다중 상속 시, 충돌 방지 => interface 를 implement 하는 방법도 있지만 상속보다는 구현에 가깝다. 구분할 때에는 단어 의미를 생각해보면 된다.




    기능 확장

    class Parent {
        public Parent() { }
        public void earnMoney() {
            System.out.println("Parent earnMoney()");
        }
    }
    
    class Child extends Parent {
        public Child() { }
        public void cookBread() {
            System.out.println("Child cookBread()");
        }
    }
    
    class Main {
        public static void main(String[] args) {
            Child child = new Child();
            child.earnMoney();
            child.cookBread();
        }
    }
    결과
    Parent earnMoney()
    Child cookBread()

    Parent class 를 상속받은 Child class 는 만들지 않은 earnMoney() 메소드를 상속을 받음으로써 사용할 수 있게 된다.

     

     

    Q. 그렇다면 같은 메소드명을 사용하게 되면 충돌이 나지 않을까? 사용을 못하게 되는 건가?

    A. 그렇지 않다. 여기에서 Override 라는 개념이 등장한다.

    오버라이딩

    class Parent {
        public Parent() { }
        public void earnMoney() {
            System.out.println("Parent earnMoney()");
        }
    }
    
    class Child extends Parent {
        public Child() { }
        @Override
        public void earnMoney() {
            System.out.println("Child earnMoney()");
        }
        public void cookBread() {
            System.out.println("Child cookBread()");
        }
    }
    
    class Main {
        public static void main(String[] args) {
            Child child = new Child();
            child.earnMoney();
            child.cookBread();
        }
    }
    결과
    Child earnMoney()
    Child cookBread()

    이번에는 Child class 에서 earnMoney() 를 만들어주었다.
    Parent class 의 earnMoney() 메소드가 아닌 Child class 의 earnMoney() 메소드가 호출되었다.
    이걸 바로 오버라이딩이라고 하며, @Override 어노테이션을 붙여준다.
    재정의 라는 의미로, 부모클래스를 상속받아 자손클래스에서 메소드를 다시 정의를 하고자 할 때 사용한다.

     

     

    Q. 어노테이션을 지워도 사용할 수 있던데 꼭 붙여서 사용해야 할까?

    A. 꼭 붙여주어야 한다.

    두 가지 이유가 있다.

    1. 개발자가 이 메소드는 재정의 되었다는 것을 한 번에 알아차릴 수 있다. (추가 제공하는 메타 데이터인 셈)
    메타데이터(Metadata)는 일반적으로 데이터에 관한 구조화된 데이터로, 대량의 정보 가운데에서 확인하고자 하는 정보를 효율적으로 검색하기 위해 원시데이터(Raw data)를 일정한 규칙에 따라 구조화 혹은 표준화한 정보를 의미이다. (출처: 국가한의임상정보포털)

    2. 컴파일 과정에서 에러를 잡아줄 수 있다.
    클래스 객체를 생성하기 위해 필요한 것은 생성자이다. 생성자는 new 연산자를 통해 호출된다.
    이 과정을 인스턴스화라고 하는데, 이 때 주소가 heap 메모리에 저장된다.
    그리고 heap 메모리는 런타임 과정에서 사용되는 것이라고 했다.
    프로그램이 실행된 후에 문제가 생길 수 있다는 말이다. 어노테이션을 쓰면, 그런 위험을 방지할 수 있다.

     

    오버라이딩 사용 시, 주의할 점

    • 리턴 타입이 동일해야한다.
    • 접근 제어자가 확대되는 것은 가능하지만, 축소할 수는 없다.


    접근 제어자 : public > protected > default > private

     

     

    Q. 그렇다면 Override 어노테이션은 어떻게 동작하는 걸까?

    Override 를 직접 열어보게 되면, @Retention 이라는 어노테이션이 눈에 띈다.

     

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.SOURCE)
    public @interface Override {
    }

    RetentionPolicy 에 대한 설명은 여기로.

     



    상속의 생성자

    class Parent {
        //public Parent() {
        //    System.out.println("Parent Constructor");
        //}
        public Parent(int x, int y) {
            System.out.println("Parent x y Constructor");
        }
    
        public void earnMoney() {
            System.out.println("Parent earnMoney()");
        }
    }
    
    class Child extends Parent {
        public Child() {
            super(); // There is no default constructor available
            System.out.println("Child Constructor");
        }
    }

    super() 를 사용하면 부모클래스의 생성자를 호출한다는 것을 의미한다.

    Parent class 에서 기본 생성자를 지우고 Child class 에 기본 생성자를 만들어줄 때 super() 를 지워보았다.
    부모클래스의 기본 생성자가 존재하지 않을 시, 위와 같은 There is no default constructor available 에러가 발생한다.

    기본 생성자가 없다는 에러 메시지를 표시해준다.

     

    class Parent {
        public Parent() {
            System.out.println("Parent Constructor");
        }
        public void earnMoney() {
            System.out.println("Parent earnMoney()");
        }
    }
    
    class Child extends Parent {
        public Child() {
            System.out.println("Child Constructor");
        }
        public void earnMoney() {
            //super.earnMoney();
            System.out.println("Child earnMoney()");
        }
    }
    결과
    Parent Constructor
    Child Constructor
    Child earnMoney()

    super().earnMoney() 로 사용하면 부모클래스의 메소드를 호출한다는 의미다.
    만약, 위의 주석을 푼 다면 아래처럼 결과가 나온다.

    Parent Constructor
    Child Constructor
    Parent earnMoney()
    Child earnMoney()

     

    1개 이상의 파라미터가 있는 생성자를 사용한다면?

    class Parent {
        //public Parent() {
        //    System.out.println("Parent Constructor");
        //}
        public Parent(int x, int y) {
            System.out.println("Parent x y Constructor");
        }
    }
    
    class Child extends Parent {
        public Child(int x, int y) {
            //super(); // cannot be applied to '()'
            super(x, y);
            System.out.println("Child x y Constructor");
        }
    }
    
    class Main {
        public static void main(String[] args) {
            Child child = new Child(1, 2);
        }
    }
    결과
    Parent x y Constructor
    Child x y Constructor

    정상 작동한다!

     

    다시 한 번 언급하자면, 클래스 객체를 생성하기 위해 필요한 것은 생성자이다.
    부모클래스를 상속받으려면 우선 부모클래스 객체를 생성해주어야한다.
    그러기 위해선 부모클래스의 생성자가 필요하다.

    상속을 받을 때에는 반드시 자손클래스에서 만들어준 생성자가 부모클래스에도 존재해야한다.

     

     

    Q. super() 가 없어도 잘 작동 되는 경우가 있을까?

    A. 있다.

    하지만 우리가 직접 입력해주지 않아도 자동으로 주입해준다는 것이 더 정확한 표현이다.
    기본 생성자도 생성자를 만들어주지 않았을 때에 자바에서 자동으로 주입해주도록 되어있다.
    상속에서도 마찬가지다.

    class Parent {
        public Parent() {
            System.out.println("Parent Constructor");
        }
        //public Parent(int x, int y) {
        //    System.out.println("Parent x y Constructor");
        //}
    }
    
    class Child extends Parent {
        public Child() {
            System.out.println("Child Constructor");
        }
        public Child(int x, int y) {
            System.out.println("Child x y Constructor");
        }
    }
    
    class Main {
        public static void main(String[] args) {
            Child child1 = new Child();
            Child child2 = new Child(1, 2);
        }
    }
    결과
    Parent Constructor
    Child Constructor
    Parent Constructor
    Child x y Constructor

    위와 같이 기본 생성자를 만들어주게 되면 super() 가 없어도 코드가 정상적으로 컴파일되어 실행된다.

    더보기

    참고 자료 : 자바의 정석, 자바의 신

    (+개인 생각)

    'Programming Lang > Java' 카테고리의 다른 글

    [Java] Polymorphism (작성중입니다)  (0) 2022.09.11
    [Java] Package, Modifier  (0) 2022.09.10
    [Java] Class, Instance, Constructor  (0) 2022.09.07
    [Java] Variable, Method, JVM  (0) 2022.09.05
    [Java] String Array  (0) 2022.08.28
Designed by Tistory.