JAVA의 가장 기본이 되는 내용

상속 (Part.1)

cwchoiit 2024. 3. 28. 08:34
728x90
반응형
SMALL

참고 자료:

 

김영한의 실전 자바 - 중급 1편 | 김영한 - 인프런

김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., [사진]국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바

www.inflearn.com

덜 중요한 부분: 규칙

클래스는 다중 상속이 불가하다 

다음 그림이 예시인데 만약, 다음 그림처럼 다중 상속을 했을 때 자식 입장에서 move()를 실행하면 어떤 부모의 move()를 실행해야 하는지 자바는 알 수 없기 때문이다. 

 

 

인터페이스는 다중 상속이 가능하다

인터페이스는 여러 인터페이스를 상속받아도 결국 구현하는 구현체에서 그 쓰임새를 만들기 때문에 위와 같은 문제가 발생하지 않아서 그렇다. 

 

 

상속 관계에서 화살표의 방향은 '내가 얘를 알고 있다'라고 해석하면 이해가 빠르다

이 그림에서 자식에서 부모 방향으로 화살표 방향이 정해지는데, 가끔 이게 헷갈렸다. 근데 확실하게 이해했다. 내가 얘를 알고 있다로 보면 바로 이해된다. 자식은 부모를 상속받는다. 그래서 자식은 부모가 누군지 명확히 알고 있다. 근데 부모는 누가 나를 상속받는지 코드 상에서 전혀 알 수 없다. 그래서 화살표 방향을 저렇게 표현한다.

 

 

매우 중요한 부분: 상속과 메모리 구조

이 부분을 제대로 이해해야 한다. 다음 코드를 보자.

ElectricCar electricCar = new ElectricCar();

 

위와 같이 ElectricCar 클래스의 객체를 생성했다. 이 클래스는 Car를 상속받는다. 이럴 때 메모리에서 어떤 구조로 만들어질까?

그림과 같이 메모리에서는 ElectricCar 뿐만 아니라 상속 관계에 있는 Car까지 함께 포함된 인스턴스를 생성한다. 참조값은 x001 하나이지만 실제로 그 안에서는 Car, ElectricCar라는 두가지 클래스 정보가 공존하는 것이다. 

 

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

 

이 상태에서 electricCar.charge()를 호출하면 어떻게 될까?

우선 참조값을 확인해서 x001.charge()를 호출하게 되고, x001을 찾아서 charge()를 호출하면 되는 건데 상속 관계의 경우엔 내부에 부모와 자식이 모두 존재한다. 이때 부모인 Car를 통해서 charge()를 찾을지 아니면 ElectricCar를 통해서 charge()를 찾을지 선택해야 한다. 이때는 호출하는 변수의 타입(클래스)을 기준으로 선택한다. electricCar 변수의 타입이 ElectricCar 이므로 인스턴스 내부에 같은 타입인 ElectricCar를 통해서 charge()를 호출한다.

 

이 내용을 토대로 electricCar.move()를 호출하면?

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

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

 

 

정리를 하자면 딱 3가지 핵심을 기억하자.

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

 

 

오버라이딩과 메모리 구조

다를건 없다. 위에서 설명한 그대로를 따라가는데 한번 보자.

1. electricCar.move()를 호출한다.

2. 호출한 electricCar의 타입은 ElectricCar이다. 따라서 인스턴스 내부의 ElectricCar 타입에서 시작한다.

3. ElectricCar 타입에 move() 메서드가 있다. 해당 메서드를 실행한다. 이때 실행할 메서드를 이미 찾았으므로 부모 타입을 찾지 않는다.

 

메서드 오버라이딩 조건

- 메서드 이름: 메서드 이름이 같아야 한다.

- 메서드 매개변수(파라미터): 매개변수(파라미터) 타입, 순서, 개수가 같아야 한다.

- 반환 타입: 반환 타입이 같아야 한다. 단, 반환 타입이 하위 클래스 타입일 수 있다.

- 접근 제어자: 오버라이딩 메서드의 접근 제어자는 상위 클래스의 메서드보다 더 제한적이어서는 안된다. 예를 들어, 상위 클래스의 메서드가 protected로 선언되어 있으면 하위 클래스에서 이를 public, protected로 오버라이딩할 수 있지만, private, default로는 안된다.

- 예외: 오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 throws로 선언할 수 없다. 하지만 더 적거나 같은 수의 예외, 또는 하위 타입의 예외는 선언할 수 없다.

- static, final, private 키워드가 붙은 메서드는 오버라이딩 될 수 없다: static은 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩이 의미가 없고, final은 더이상 변경이 불가능함을 의미하고, private은 아예 자식 클래스에서는 보이지도 않는다.

- 생성자 오버라이딩은 불가하다.

 

 

상속과 접근제어

당연히 상속받아도 접근 제어자에 의해 접근이 가능하고 불가능하다. 단순하게 접근 제어자 그대로 따라가면 된다.

- public: 상속받은 클래스에서 접근할 수 있다.

- private: 상속받은 클래스에서 접근 불가하다.

- default: 같은 패키지의 상속받은 클래스면 접근이 가능하다.

- protected: 상속받은 클래스에서 접근이 가능하다.

 

 

728x90
반응형
LIST

'JAVA의 가장 기본이 되는 내용' 카테고리의 다른 글

다형성 (Part.1) 매우중요 ✨  (0) 2024.03.28
상속 (Part.2)  (0) 2024.03.28
final  (0) 2024.03.27
자바 메모리 구조 ✨  (0) 2024.03.27
캡슐화  (0) 2024.03.26