회원가입

상속 - 정의

Beany 2024-09-19

ChatGPT 요약

이 글은 상속 개념과 필요성, 객체 지향 프로그래밍의 핵심 개념인 상속에 대해 설명하고 있다. 코드 예제를 통해 전기차와 가솔린차의 상속 관계를 보여준 후, 상속의 메모리 구조와 호출 방식을 설명하고 있다. 상속을 통해 부모 클래스의 필드와 메서드를 새로운 클래스에서 재사용할 수 있으며, 객체를 생성할 때 내부에는 부모와 자식이 함께 생성된다. 또한, 호출할 때는 변수의 타입을 기준으로 기능을 선택하며, 상속 관계에서 부모 타입으로 올라가며 필요한 기능을 찾는다. 인터페이스를 통해 다중 구현을 허용하고 다중 상속을 허용하지 않는 점에 대해서도 언급하고 있다.

상속 관계가 왜 필요한지 이해하기 위해 다음 예제 코드를 만들어서 실행해보자.

 

예제 코드

package extends1.ex1;

public class ElectricCar {
    public void move() {
        System.out.println("move");
    }
    
    public void charge() {
        System.out.println("charge");
    }
}
package extends1.ex1;

public class GasCar {
    public void move() {
        System.out.println("move");
    }

    public void fillUp() {
        System.out.println("fill up");
    }
}
package extends1.ex1;

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();
    }
}
move
charge
move
fill up

전기차(ElectricCar)가솔린차(GasCar)를 만들었다.
전기차이동(move()) 충전(charge()) 기능이 있고, 가솔린차이동(move()) 주유(fillUp()) 기능이 있다.

전기차가솔린차자동차(Car)의 좀 더 구체적인 개념이다. 반대로 자동차(Car)전기차가솔린차를 포함하는 추상적인 개념이다. 그래서인지 잘 보면 둘의 공통 기능이 보인다. 바로 이동(move()) 이다.

전기차가솔린차든 주유하는 방식이 다른 것이지 이동하는 것은 똑같다. 이런 경우 상속 관계를 사용하는 것이 효과적이다.

 

 

상속 관계

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

상속을 사용하려면 extends 키워드를 사용하면 된다. 그리고 extends 대상은 하나만 선택할 수 있다.

 

[ 용어 정리 ]

  • 부모 클래스 (슈퍼 클래스): 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공하는 클래스
  • 자식 클래스 (서브 클래스): 부모 클래스로부터 필드와 메서드를 상속받는 클래스

 

상속 관계를 사용하도록 코드를 작성해보자.

package extends1.ex2;

public class Car {
    public void move() {
        System.out.println("move");
    }
}

Car부모 클래스가 된다. 여기에는 자동차의 공통 기능인 move() 가 포함되어 있다.

 

package extends1.ex2;

public class ElectricCar extends Car {
    public void charge() {
        System.out.println("charge");
    }
}
package extends1.ex2;

public class GasCar extends Car {
    public void fillUp() {
        System.out.println("fill up");
    }
}
package extends1.ex2;

public class CarMain {
    public static void main(String[] args) {
        GasCar gasCar = new GasCar();
        gasCar.move();
        gasCar.fillUp();

        ElectricCar electricCar = new ElectricCar();
        electricCar.move();
        electricCar.charge();
    }
}
move
fill up
move
charge

전기차와 가솔린차가 Car 를 상속 받은 덕분에 electricCar.move() gasCar.move() 를 사용할 수 있다.

참고로 당연한 이야기지만 상속은 부모의 기능을 자식이 물려 받는 것이다. 따라서 자식이 부모의 기능을 물려 받아서 사용할 수 있다. 반대로 부모 클래스는 자식 클래스에 접근할 수 없다. 자식 클래스는 부모 클래스의 기능을 물려 받기 때문에 접근할 수 있지만, 그 반대는 아니다. 부모 코드를 보자! 자식에 대한 정보가 하나도 없다. 반면에 자식 코드는 extends Parent 를 통해서 부모를 알고 있다.

 

단일 상속

참고로 자바는 다중 상속을 지원하지 않는다. 그래서 extend 대상은 하나만 선택할 수 있다. 부모를 하나만 선택할 수 있다는 뜻이다. 물론 부모가 또 다른 부모를 하나 가지는 것은 괜찮다.

 

다중 상속 그림

만약 비행기와 자동차를 상속 받아서 하늘을 나는 자동차를 만든다고 가정해보자. 만약 그림과 같이 다중 상속을 하게 되면 AirplaineCar 입장에서 move() 를 호출할 때 어떤 부모의 move() 를 사용해야 할지 애매한 문제가 발생한다. 이걸을 다이아몬드 문제라 한다. 그리고 다중 상속을 사용하면 클래스 계층 구조가 매우 복잡해질 수 있다. 이런 문제점 때문에 자바는 클래스의 다중 상속을 허용하지 않는다. 대신 이후에 설명할 인터페이스다중 구현을 허용해서 이러한 문제를 피한다.

 

 

상속과 메모리 구조

상속 관계를 객체로 생성할 때 메모리 구조를 확인해보자.

ElectricCar electricCar = new ElectricCar();

new ElectricCar() 를 호출하면 ElectricCar 뿐만 아니라 상속 관계에 있는 Car까지 함께 포함해서 인스턴스를 생성한다.

참조값은 x001(랜덤) 하나이지만 실제로 그 안에는 Car, ElectricCar 라는 두가지 클래스 정보가 공존하는 것이다.

상속이라고 해서 단순하게 부모의 필드와 메서드만 물려 받는 게 아니다. 상속 관계를 사용하면 부모 클래스도 함께 포함해서 생성된다. 외부에서 볼때는 하나의 인스턴스를 생성하는 것 같지만 내부에서는 부모와 자식이 모두 생성되고 공간도 구분된다.

electricCar.charge() 를 호출하면 참조값을 확인해서 x001.charge() 를 호출한다. 따라서 x001 을 찾아서 charge() 를 호출하면 되는 것이다. 그런데 상속 관계의 경우에는 내부에 부모와 자식이 모두 존재한다. 이때 부모인 Car 를 통해서 charge() 를 찾을지 아니면 ElectricCar 를 통해서 charge() 를 찾을지 선택해야 한다.

이때는 호출하는 변수의 타입(클래스)을 기준으로 선택한다. electricCar 변수의 타입이 ElectricCar 이므로 인스턴스 내부에 같은 타입인 ElectricCar 를 통해서 charge() 를 호출한다.

electricCar.move() 를 호출하면 먼저 x001 참조로 이동한다. 내부에는 Car, ElectricCar 두가지 타입이 있다. 이때 호출하는 변수인 electricCar 의 타입이 ElectricCar 이므로 이 타입을 선택한다. 그런데 ElectricCar 에는 move() 메서드가 없다. 상속 관계에서 자식 타입에 해당 기능이 없으면 부모 타입으로 올라가서 찾는다. 이 경우 ElectricCar 의 부모인 Car 로 올라가서 move() 를 찾는다. 부모인 Car 에 move() 가 있으므로 부모에 있는 move() 메서드를 호출한다.

만약 부모에서도 해당 기능을 찾지 못하면 더 상위 부모에서 필요한 기능을 찾아본다. 부모에 부모로 계속 올라가면서 필드나 메서드를 찾는 것이다. 물론 계속 찾아도 없으면 컴파일 오류가 발생한다.

 

지금까지 설명한 상속과 메모리 구조는 반드시 이해해야 한다.

  • 상속 관계의 객체를 생성하면 그 내부에는 부모와 자식이 모두 생성된다.
  • 상속 관계의 객체를 호출할 때, 대상 타입을 정해야 한다. 이때 호출자의 타입을 통해 대상 타입을 찾는다.
  • 현재 타입에서 기능을 찾지 못하면 상위 부모 타입으로 기능을 찾아서 실행한다. 기능을 찾지 못하면 컴파일 오류가 발생한다.
0 0
JAVA
이 공간은 개인 공부를 통해 얻은 정보를 체계적으로 정리하고 공유하는 곳입니다. 학습한 내용, 발견한 지식, 그리고 문제 해결 방법 등을 기록하여 나만의 학습 자료를 구축하고, 필요할 때 쉽게 참고할 수 있는 유용한 자원으로 활용할 수 있도록 합니다.
Yesterday: 456
Today: 215