Java/김영한의 실전자바

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

슈코 2024. 1. 21. 21:26

상속

  • 기존 클래스의 속성과 기능을 그대로 물려받는다
  • 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다
  • extends 키워드를 사용한다(extends 대상은 하나만 선택 가능, 다중 상속 불가)
  • 부모 클래스(슈퍼 클래스) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에게 제공
  • 자식 클래스(서브 클래스) : 부모 클래스로부터 필드와 메서드를 상속받는 클래스

 

상속 예제

  • Car 클래스는 move() 라는 메소드를 가진다.
  • ElectricCar 클래스는 move() 메소드를 사용할 수 있고, charge() 메소드를 가진다.
  • GasCar 클래스는 move() 메소드를 사용할 수 있고, fillUp() 메소드를 가진다.
  • Car 클래스를 부모 클래스로, ElectricCar, GasCar 클래스를 자식 클래스로 정의한다.
  • 자식은 부모 클래스의 필드와 메소드를 사용할 수 있다.

 

Car 클래스

public class Car {

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

    public void openDoor() {
        System.out.println("문을 엽니다.");
    }
}

 

 

ElectricCar 클래스(Car 클래스 상속)

public class ElectricCar extends Car {

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

 

 

GasCar(Car 클래스 상속)

public class GasCar extends Car {

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

 

 

상속 기능 확인 - Main

public class CarMain {

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

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

ElectricCar 클래스가 Car 클래스의 move(), openDoor() 메소드를 활용할 수 있다

GasCar 클래스도 Car 클래스의 move() 메소드를 사용할 수 있다

물론, 자기가 가지고 있는 메소드들은 무조건 사용할 수 있다

 

상속은 특정 클래스의 기능은 그대로 활용할 수 있으면서, 자기 자신의 기능만을 추가하기에 좋은 옵션이다

또한, 부모 클래스에 기능을 추가하면 그 자식들은 모두 그 기능을 활용할 수도 있다

 

 

단일 상속

  • 자바는 다중 상속을 지원하지 않는다(extends의 대상은 하나만 선택 가능)
    • 다이아몬드 문제
      • AriPlane 클래스에 move() 메소드가 있고, Car() 클래스에도 move() 메소드가 있다
      • AirPlaneCar 라는 클래스를 정의하고 두 클래스를 모두 상속하려고 하면 move() 메소드는 어떤걸 써야하는지 모름
  • 인터페이스의 다중 구현을 통해서 이런 문제를 피할 수 있다.

 

 

상속과 메모리 구조

  • Car 클래스의 자식 클래스 ElectricCar 클래스를 인스턴스화하면 Car 클래스까지 포함해서 인스턴스를 생성한다.
    • 참조값은 하나지만, 그 안에는 Car, ElectricCar 두가지 클래스 정보가 공존
    • 상속 관계를 사용하면, 부모 클래스도 함께 생성
  • 메소드 호출 시 변수의 타입(클래스)를 기준으로 선택
    • new ElectricCar() - 자식 메소드 호출 시, ElectricCar의 메소드를 호출
    • new ElectricCar() - 부모 메소드 호출 시, ElectricCar에 메소드 있는지 확인(없으면 부모 클래스 메소드 호출)
      • 오버라이딩된 메소드가 우선 순위를 가진다
      • 당연히 내 메소드가 있으면 그걸 쓰고, 없으면 부모의 메소드를 호출
    • 부모에서도 못 찾으면, 상위 부모로 올라가서 찾는다(끝까지 못찾으면 컴파일 오류 발생)

 

상속과 메소드 오버라이딩

  • 부모 타입의 기능을 자식이 재정의 하는 것을 오버라이딩
    • 부모의 기능을 그대로 사용하고 싶지 않을때 활용
    • 오버라이딩은 강제적인 조건은 아니기에, 자바는 다중상속이 안되는 부분(이 문제는 인터페이스로 해결)
  • @Override 애노테이션 : 상위 클래스의 메소드를 오버라이딩 하는 것을 표현
  • 오버라이딩 조건을 만족하지 않으면, 컴파일 에러 발생(실수 방지)
  • 자식에 move() 메소드를 오버라이딩 하는데, 부모에 move()가 없으면 에러

 

 

오버라이딩 조건

  • 같은 껍데기
    • 메소드 이름이 같아야 한다
    • 매개변수(파라미터) 타입, 순서, 갯수가 같아야 한다
    • 반환 타입이 같아야 한다(하위 클래스 타입 가능)
  • 예외 : 오버라이딩 메소드는 상위 클래스의 메소드보다 더 많은 체크 예외를 throws로 선언할 수 없다(더 적거나, 같은 수)
  • static, final, private 키워드가 붙으면 오버라이딩 할 수 없다
    • static: 오버라이딩은 인스턴스 단계에서 활용되는데, static은 클래스 단계에서 사용되어지므로 의미가 없다
    • final: final은 재정의를 금지하고 있다
    • private: 해당 클래스만 접근하므로, 하위 클래스가 접근할 수 없다

 

 

super - 부모 참조

  • 부모와 자식이 필드명이 같거나, 메소드 오버라이딩이 되어 있으면 자식에서 부모의 필드나 메소드를 참조 할 수 없다
    • 기본적으로 나의 필드명이나, 메소드를 호출하기 때문
  • super 키워드 사용 시 부모 참조 가능
    • 말 그대로 부모 클래스에 대한 참조
    • super.부모필드명, super.부모메소드명

 

 

super - 생성자

  • 상속 관계의 인스턴스 생성 시 메모리 내부에는 자식과 부모 클래스가 모두 생성되기에 각각의 생성자도 모두 호출되어야 한다
  • 상속 관계 사용 시, 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출
  • super() 로 부모 클래스의 생성자 호출(부모 클래스가 기본 생성자인 경우 super() 생략 가능)
  • 상속 관계에서 자식 클래스의 생성자 첫 줄에 super() 를 호출해야 한다(부모부터 초기화)