처누

[김영한의 실전 자바 - 기본편] 상속 본문

java

[김영한의 실전 자바 - 기본편] 상속

처누 2024. 1. 7. 13:47

상속이란?

 '상속'은 객체 지향 프로그래밍의 핵심 요소 중 하나이다. 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다. 이름 그래도 기존 클래스의 속성과 기능을 그대로 물려받는 것이다. 아래 예제로 쉽게 이해해보자.

 

public class CarMain {

    public static void main(String[] args) {
        ElectricCar electricCar = new ElectricCar();
        electricCar.move();
        electricCar.charge();

        GasCar gasCar = new GasCar();
        gasCar.move();
        gasCar.fillUp();
    }
}

public class ElectricCar {

    public void move() {
        System.out.println("차를 이동합니다.");
    }

    public void charge() {
        System.out.println("충전합니다.");
    }
}

public class GasCar {

    public void move() {
        System.out.println("차를 이동합니다.");
    }

    public void fillUp() {
        System.out.println("기름을 주유합니다.");
    }
}

 위 코드에서 ElectriCar클래스와 GasCar클래스에서 move()메서드를 공통으로 가지고 있다. 이런 경우 상속 관계를 사용하는 것이 효과적이다.

public class Car {

    public void move() {
        System.out.println("차를 이동합니다.");
    }
}

public class ElectricCar extends Car{

    public void charge() {
        System.out.println("충전합니다.");
    }
}

public class GasCar extends Car{

    public void fillUp() {
        System.out.println("기름을 주유합니다.");
    }
}

 위 코드와 같이 부모클래스인 Car클래스에서 공통으로 사용됐던 move()메서드를 갖고, ElectricCar와 GasCar는 주유하는 방식에 관련된 메서드가 갖게된다.

 상속 관계를 객체로 생성할 때 메모리 구조는 다음과 같다.

 상속 관계의 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성된다. 상속 관계의 객체를 호출할 때, 대상 타입을 정해야 한다. 이때 호출자의 타입을 통해 대상 타입을 찾는다. 현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행한다. 기능을 찾지 못하면 컴파일 오류가 발생한다.

 위 코드에서 새로운 자동차 클래스를 추가하거나, 모든 차량에 적용되는 공통되는 기능을 추가하는 경우 상속 관계의 장점을 극대화 할 수 있다. Car클래스에 공통되는 기능을 추가하게 되면 ElectricCar, GasCar에서 수정사항 없이 그 기능을 사용할 수 있다. 새로운 자동차 클래스를 추가하는 경우에도 그 자동차가 가지는 고유 기능만 구현하고 Car클래스를 상속하게 되면 Car클래스 내부의 메서드를 사용할 수 있다.

 

오버로딩과 오버라이딩

오버로딩(Overloading) : 메서드 이름이 같고 매개변수(파라미터)가 다른 메서드를 여러개 정의하는 것
오버라이딩(Overriding) : 하위 클래스에서 상위 클래스의 메서드를 재정의하는 과정을 의미.

 

super - 부모 참조

 부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있으면, 자식에서 부모의 필드나 메서드를 호출할 수 없다. 이때 super 키워드를 사용하면 부모를 참조할 수 있다.

 

public class SuperMain {

    public static void main(String[] args) {
        Child child = new Child();
        child.call();
    }
}

public class Parent {

    public String value = "parent";

    public void hello() {
        System.out.println("Parent.hello");
    }
}

public class Child extends Parent{

    public String value = "child";

    @Override
    public void hello() {
        System.out.println("Child.hello");
    }

    public void call() {
        System.out.println("this value = " + this.value);
        System.out.println("super value = " + super.value);

        this.hello();
        super.hello();
    }
}

 위 코드에서 Child클래스는 Parent클래스를 상속받고 hello()메서드를 오버라이딩했다. call()메서드에서 super를 이용하여 부모 클래스가 갖는 value값과 hello()를 호출할 수 있다.

 하지만 super생성자를 호출하는 경우에는 주의해야할 사항이 있다. 기본 생성자(파라미터가 없는 생성자)인 겨웅에는 super()를 생략할 수 있지만 부모 클래스가 파라미터가 없는 생성자가 없고 생성자가 파라미터를 갖는다면 super()를 꼭 구현해야 한다. 아래 코드를 보면 쉽게 이해할 수 있다.

public class ClassA {

    public ClassA() {
        System.out.println("ClassA 생성자");
    }
}

public class ClassB extends ClassA{

    public ClassB(int a) {
        super(); //생략 가능
        System.out.println("ClassB 생성자 a=" + a);
    }

    public ClassB(int a, int b) {
        super(); //생략 가능
        System.out.println("ClassB 생성사 a=" + a + ", b=" + b);
    }
}

public class ClassC extends ClassB{

    public ClassC() {
//        super(); //기본 생성자인 경우에만 가능
        super(10, 20);
        System.out.println("ClassC 생성자");
    }
}