Java/[인프런 김영한 실전 자바 - 기본편]

[인프런 김영한 실전 자바 - 기본편] 상속

h2boom 2024. 7. 16. 18:22

상속

  • 상속은 객체 지향 프로그래밍의 핵심 요소 중 하나
  • 기존 클래스의 필드와 메소드를 새로운 클래스에서 재사용하게 해준다.
    • 기존 클래스의 속성과 기능을 그대로 물려받는 것.
    • 모든 자식 클래스에게 필요한 기능은 부모 클래스에만 추가하면 된다.
      • 상속 관계 덕분에 공통 기능을 부모에서 관리하므로 중복을 줄일 수 있다.
  • 상속을 사용하려면 extends 키워드를 사용하며 대상은 하나만 선택할 수 있다.

 

  • 부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 필드와 메소드를 다른 클래스에 제공하는 클래스
  • 자식 클래스 (서브 클래스) : 부모 클래스로부터 필드와 메소드를 상속받는 클래스
    • 부모의 기능을 상속 받는다 = 부모의 기능을 확장한다 두 가지 표현으로 사용한다.
  • 화살표 방향이 자식 -> 부모로 향하고 있다.
    • 상속 받는 자식 클래스에서 extends 키워드를 사용해 부모 클래스를 표현하므로
      ex) Child extends Parent == Child -> Parent와 같은 의미
    • 부모 클래스에서는 자식 클래스를 모르지만 자식 클래스에서는 부모 클래스를 명시해놨기에 알 수 있다.
      • 화살표는 "내가 상대방을 알고 있다"의 의미로 자식은 부모를 알고 있기 때문에 자식->부모 형태가 된다.
  • 자식 클래스는 부모 클래스의 기능을 물려받을 수 있다.
    • 반대로 부모 클래스는 자식 클래스에 접근할 수 없다.

 

  • 자바에서는 다중 상속을 지원하지 않는다.
    • extends의 대상은 단 하나만 선택할 수 있다.
    • 하나의 부모는 여러 자식이 있을 수 있지만 한 자식에게는 한 부모만 있을 수 있다.
  • 위 예시처럼 다중 상속의 경우 자식이 move()를 호출할 경우 어느 부모의 move()를 사용해야 하는지 모호한 문제가 발생할 수 있다.
    • 이런 문제를 다이아몬드 문제라고 한다.
    • 또한 다중 상속 시 클래스 계층 구조가 매우 복잡해지기 때문에 자바에서는 다중 상속을 허용하지 않는다.

상속과 메모리 구조

//Car를 상속받는 ElectricCar 클래스가 있다고 가정
ElectricCar electricCar = new ElectricCar();
  • 자식 클래스의 객체 생성 시 ElectricCar 뿐만 아니라 부모 클래스의 Car까지 함께 인스턴스를 생성한다.
    • 참조 값은 하나지만 그 안에 두 가지 클래스 정보가 공존한다.
  • 상속은 단순히 부모의 필드, 메소드만 상속받는 것이 아닌 부모 클래스의 인스턴스도 함께 포함해서 생성된다.
    • 외부에서는 하나의 인스턴스를 생성하는 것처럼 보이지만 내부에서는 부모와 자식이 모두 생성되고 공간도 구분되어 있다.
    • 즉, 상속 관계의 객체를 생성하면 그 내부에는 부모와 자식 인스턴스가 모두 생성된다.
  • 상속 관계인 경우에 메소드 호출시 어떻게 되는가?
    • 호출하는 변수의 타입(클래스)을 기준으로 선택한다.
      • 만약 자식 클래스의 타입이면 먼저 자식 클래스에서 동일한 인스턴스 메소드가 있는지 확인 후 있다면 실행시키고 없다면 부모 클래스에서 찾아서 실행한다. 
        부모 클래스에도 없다면 더 상위 부모에서 찾으며 계속해서 없다면 컴파일 오류가 발생한다.
    • 상속 관계의 객체를 호출할 때, 대상 타입을 정하고 호출자의 타입을 통해 대상 타입을 찾는다.
      • 현재 타입에서 기능을 찾지 못하면 상위 부모 타입에서 기능을 찾아서 실행한다.


상속과 메소드 오버라이딩

  • 부모에게 상속 받은 기능을 자식이 재정의하는 것을 메소드 오버라이딩(Overrding)이라고 한다.
    • 부모 클래스의 메소드 명과는 같되, 다른 기능으로 재정의하는 것
  • 메소드 오버라이딩 시 @Override를 메소드 선언부 위에 붙여준다.
    • @Override는 상위 클래스의 메소드를 오버라이드하는 것을 알려주는 어노테이션이다.
      • 어노테이션 : 주석과 비슷하며 프로그램이 읽을 수 있는 특별한 주석과 같다.
    • @Override 어노테이션은 생략이 가능하지만 써주는 것이 좋다.
      • 부모 클래스의 메소드를 오버라이드하는 것을 컴파일러에게 알려줌으로 오버라이딩 조건을 만족시키지 않으면 오류를 통해 알려주기에 실수를 방지할 수 있다.

 

  • 메소드 오버로딩 vs 메소드 오버라이딩
    • 메소드 오버로딩(과적): 메소드 이름이 같고 매개변수가 다른 메소드를 여러개 정의하는 것.
    • 메소드 오버라이딩(재정의): 하위 클래스에서 상위 클래스의 메소드를 재정의하는 것.

 

  • 메소드 오버라이딩의 조건
    • 메소드 명: 메소드 이름이 같아야 한다.
    • 메소드 매개 변수: 메소드 매개 변수의 타입, 순서, 개수가 같아야 한다.
    • 리턴 타입: 리턴 타입이 같아야 한다.
    • 접근 제어자: 상위 클래스의 메소드보다 더 제한적인 접근제어자를 사용해서는 안된다.
      ex) 상위 클래스 protected -> 하위 클래스 protected, public만 가능하다.
    • 예외: 상위 클래스의 메소드보다 더 많은 체크 예외를 throws로 선언할 수 없다.
    • static, final, private 키워드가 붙은 메소드는 오버라이딩 할 수 없다.
      • static 메소드는 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩이 의미가 없다.
      • final 메소드는 재정의를 금지한다.
      • private 메소드는 해당 클래스에서만 접근 가능하므로 하위 클래스에서 보이지 않아서 오버라이딩 할 수 없다.
    • 생성자: 생성자는 오버라이딩 할 수 없다.

 

 

  • 상속 관계에서는 부모 클래스의 public, protected로 선언된 변수, 메소드만 호출할 수 있다.

super

  • 부모와 자식의 필드명이 같거나 오버라이딩되어 있으면 자식에서 부모의 필드나 메소드를 호출할 수 없다.
    • 이때 super 키워드를 사용하면 부모를 참조할 수 있다.
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); //this 생략 가능
        System.out.println("super.value = " + super.value);

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

 

  • this는 자기 자신의 참조를 의미하며 생략할 수 있다.
    • this가 생략됐다면 먼저 자신의 클래스에서 찾아보고 없으면 상위 클래스에서 찾는다.
  • super는 부모 클래스에 대한 참조를 의미한다.
    • 필드 이름과 메소드 이름이 같아도 super를 사용해 부모 클래스의 필드와 메소드를 사용할 수 있다.

 

  • 상속 관계의 인스턴스를 생성하면 부모 클래스와 자식 클래스의 인스턴스가 각각 다 생성된다.
    • 즉, 각각의 생성자도 모두 호출되어야 한다.
    • 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다.
  • 상속 관계에서 부모 클래스의 생성자를 호출할 때는 super(...)를 사용한다.
    • 부모 클래스의 기본 생성자를 호출할 경우 super()는 생략할 수 있다.
      • super(...) 생략시 자동으로 기본 생성자 super()를 만들어준다.
    • 매개 변수가 있는 생성자를 호출하는 경우 직접 작성해줘야 한다.

 

class Child extends Parent{

	public Child() {
		this("hi");
	}

	public Child(String name) {
		super();
	}

}
  • super(...)는 자식 생성자 안에서 반드시 첫 줄에 호출해야한다.
    • this(...)와 super(...)는 둘 다 첫 줄에 명시해야 하므로 함께 사용할 수 없다.
      하지만 위 예시와 같이 사용할 수 있으며 this(...)를 사용하더라도 마지막에는 반드시 super(...)를 첫 줄에 호출해야한다.
  • 상속 관계에서 생성자 호출은 부모에서 자식 순서로 실행된다.
    • 생성자로 초기화 시 부모의 데이터를 먼저 초기화하고 자식의 데이터를 초기화한다.

 

  • final 클래스
    • final로 정의된 클래스를 다른 클래스에서 상속받을 수 없다.

 

출처:[인프런 김영한 실전 자바 - 기본편]

https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

김영한의 실전 자바 - 기본편 강의 | 김영한 - 인프런

김영한 | 실무에 필요한 자바 객체 지향의 핵심 개념을 예제 코드를 통해 쉽게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문

www.inflearn.com