[JAVA]김영한의 실전 자바 ; 상속

선뀰·2024년 1월 5일
0

JAVA

목록 보기
24/25

상속관계

상속은 부모 클래스의 멤버(필드, 메서드, 이너 클래스)를 내려받아 자식 클래스 내부에 포함시키는 자바 문법요소이다.

객체지향 프로그래밍의 핵심 요소 중 하나로, 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다. 기존 클래스의 속성과 기능을 그대로 물려받는 것이다.
상속을 사용하려면 extends 키워드를 사용하면 된다. 대상은 하나만 선택할 수 있다. 다중상속이 불가능!

용어 정리

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

상속 구조도


전기차가 가솔린차가 Car를 상속 받은 덕분에 electricCar.move(), gasCar.move()를 사용할 수 있다. 상속은 부모의 기능을 자식이 물려 받는 것이다. 자식이 부모의 기능을 물려 받아서 사용할 수 있다 반대로 부모 클래스는 자식 클래스에 접근할 수 없다. 자식 클래스는 부모 클래스의 기능을 물려받기 때문에 접근할 수 있다. 자식 코드는 extends Parent를 통해서 부모를 알고 있다.

단일 상속

자바는 다중 상속을 지원하지 않는다. extend대상은 하나만 선택할 수 있다. 부모를 하나만 선택할 수 있다는 듯이다. 부모가 또 다른 부모를 하나 가지는 것은 괜찮다.

상속과 메모리 구조

ElectricCar 뿐만 아니라 상속 관계에 있는 Car까지 포함해서 인스턴스를 생성한다.
Car, ElectricCar라는 두가지 클래스 정보가 공존하는 것이다.
상속이라고 해서 단순하게 부모의 필드와 메서드만 물려 받는것이 아니다.
상속 관계를 사용하면 부모 클래스도 함께 포함해서 생성된다. 자식과 부모가 둘 다 생성이 된다.

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

상속과 기능 추가

public class HydrogenCar extends Car{
    public void fillHydrogen() {
        System.out.println("수소를 충전합니다.");
    }
}

HydrogenCar hydrogenCar = new HydrogenCar();
        hydrogenCar.move();
        hydrogenCar.fillHydrogen();
        hydrogenCar.openDoor();

extends를 통해 Car를 상속받는다.
hydrogenCar의 기능을 추가한다.

클래스의 업캐스팅 및 다운캐스팅

  • 업캐스팅(자동 변환)
    A ac = (A) new C(); // C->A로 업캐스팅(자동 변환)을 한다.
    B bc = (B) new C(); // C->B로 업캐스팅(자동 변환)을 한다.

  • 다운캐스팅 : 캐스팅할 수 없을 때(실행할 때 예외 발생)
    A aa=new A();

  • 다운캐스팅 : 캐스팅이 가능할 때
    A ab=new B();
    B b=(B) ab; // A->B다운캐스팅(수동 변환)

캐스팅 가능 여부 확인 instanceof 키워드

실제 객체를 어떤 생성자로 만들었는지 클래스 사이의 상속 관계를 알아야 한다.

상속과 메서드 오버라이딩

@Override를 작성한다.
부모 클래스에게 상속받은 메서드와 동일한 이름의 메서드를 재정의 하는 것이다. @이 붙은 부분을 어노테이션이라 한다. 주석과 비슷한 역할을 하고, 프로그램이 읽을 수 있는 특별한 주석이라고 생각하면 된다. 어노테이션이 없어도 상관없다. 메서드가 정확하게 오버라이드 되어 있어야 한다 아니면 오류를 나타낸다고 알려준다.

  • 어노테이션은 상위 클래스의 메서드를 오버라이드하는 것이다.
  • 오버라이딩한 메서드 위에 이 어노테이션을 붙여야 한다.
  • 컴파일러는 이 어노테이션을 보고 메서드가 정확히 오버라이드 되었는지 확인한다.
    오버라이딩 조건을 만족시키지 않으면 에러를 발생시킨다. 실수로 오버라이딩 못하는 경우를 방지한다.

메서드 오버로딩

메서드 이름이 같고 매개변수(파라미터)가 다른 메서드를 여러개 정의하는 것
같은 이름의 메서드를 여러 개 정의했다고 이해하면 된다. 부모 메서드와 같은 메서드를 오버라이딩 할 수 있다.

메서드 오버라이딩 = 메서드 재정의

하위클래스에서 상위 클래스의 메서드를 재정의하는 과정을 의미한다.
상속 관계에서 사용한다. 부모의 기능을 자식이 정의하는 것이다. 자식의 새로운 기능이 부모의 기능을 넘어서 덮어버린다.

메서드 오버라이딩 조건

  • 메서드 이름 : 같아야 한다.
  • 메서드 매개변수(파라미터) : 파라미터 타입, 순서, 개수가 같아야 한다.
  • 반환 타입 : 반환 타입이 같아야 한다. 반환 타입이 하위 클래스 타입일 수 있다.
  • 접근 제어자 : 오버라이딩 메서드의 접근 제어자는 상위 클래스의 메서드보다 더 제한적이면 안된다.
  • 예외 : 오버라이딩 메서드는 상위 클래스 메서드보다 더 많은 예외를 throws로 선언할 수 없다. 더 적거나 같은 수의 예외, 하위 타입의 예외는 선언할 수 있다.
  • static, final, private : 키워드가 붙은 메서드는 오버라이딩 될 수 없다.
    static은 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩이 의미가 없다. 클래스 이름을 통해 필요한 곳에 직접 접근하면 된다.
    final 메서드는 재정의를 금지한다.
    private 메서드는 해당 클래스에서만 접근 가능하기 때문에 하위 클래스에서 보이지 않는다. 따라서 오버라이딩 할 수 없다.
  • 생성자 오버라이딩 : 생성자는 오버라이딩 할 수 없다.

상속과접근 제어

(+) : public
(#) : protected
(~) : default
(-) : private

- 접근 제어자의 종류
private : 모든 외부 호출을 막는다.
default (package-private) : 같은 패키지안에서 호출은 허용한다.
protected : 같은 패키지안에서 호출은 허용한다. 패키지가 달라져도 상속 관계의 호출은 허용한다.
public : 모든 외부 호출을 허용한다.

private -> default -> protected -> public이 가장 많이 허용한다.

publicValue : 부모의 public 필드에 접근이 가능하다.
protectedValue = 1 : 부모의 protected가 상속 관계이므로 접근할 수 있다.
defaultValue = 1 : 부모의 default 필드에 접근하면 자식과 부모가 다른 패키지이므로 접근할 수 없다.
privateValue = 1 : 부모의 private 필드에 접근한다. 모든 외부 접근을 막으므로 자식이라도 호출할 수 없다.

- 접근 제어와 메모리 구조

본인 타입에 없다면 부모 타입에서 기능을 찾는데, 접근 제어자가 영향을 준다. 객체 내부에서는 자식과 부모가 구분되어 있기 때문이다. 자식 타입에서 부모 타입의 기능을 호출할 때, 부모 입장에서 보면 외부에서 호출한 것과 같다.

super - 부모 참조

부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있으면, 자식에서 부모의 필드나 메서드를 호출할 수 없다. 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 = " + value);
        System.out.println("super value = " + super.value);

        this.hello(); // this 생략 가능 -> 자기 자신에서 찾고, 부모요소에서 찾는다.
        super.hello();
    }

}

this는 자기 자신의 참조를 뜻한다. 생략이 가능하다.
super는 부모 클래스에 대한 참조를 뜻한다.
필드 이름과 메서드 이름이 같지만 super를 사용해서 부모 클래스에 있는 기능을 사용할 수 있다.

super - 생성자

상속 관계를 사용하면 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다.
super가 있을 경우 super부터 실행이 된다.

  • 부모가 기본 생성자가 없을 경우
public class ClassC extends ClassB{

    public ClassC() {
        super(10, 20); // 부모가 기본 생성자가 없다면 내가 직접 정의해야 한다.
        System.out.println("ClassC 생성자");
    }
}
  • 상속을 받으면 필수로 해야 하는 것
    생성자의 첫줄에 super()를 사용해서 부모 클래스의 생성자를 호출해야 한다.
    예외로 생성자 첫줄에 this()를 사용할 수는 있다. 하지만 super()는 자식의 생성자 안에서 반드시 호출해야 한다.

cf. 부모 클래스의 생성자가 기본 생성자(파라미터가 없는 생성자)인 경우에는 super()를 생략할 수 있다. 상속 관계에서 첫줄에 super()를 생략하면 자바는 부모의 기본 생성자를 호출하는 super()를 자동으로 만들어 준다.

profile
공부 기록

0개의 댓글