본문 바로가기
JAVA/ETC

[객체 지향 4대 특성 - 캡!상추다] 2.상속:재사용+확장

by 자이구 2023. 11. 28.

계층도/조직도

우리가 일반적으로 알고 있는 상속의 개념도이다. 하지만 객체 지향의 상속은 이런 개념이 아니다.

 

분류도

객체 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)하고 거기에 더해 필요한 특성을 추가, 즉 확장해서 사용할 수 있다는 의미이다. 

 

사람들은 클래스의 특성을 상속하는 말에서 특성을 빼고 클래스를 상속한다는 뜻으로 오해하니 부조-자식 관계가 나와 버린 것이다. 앞으로는 부모-자식 클래스 표현보다는 상위-하위 클래스 혹은 슈퍼-서브 클래스라 표현하자.

앞으로 객체 지향에서 상속이란 확장, 세분화, 슈퍼-서브블래스 개념으로 이해하자.

상위 클래스쪽으로 갈수록 추상화, 일반화됐다고 말하며, 하위 클래스쪽으로 갈수록 구체화, 특수화됐다고 말한다. 


 

상속관계에서 반드시 만족해야 할 문장이 있다.

하위 클래스는 상위 클래스이다.  객체 지향 설계 5원칙 LSP(리스코프 치환 원칙)을 나타내는 말이다. 

이를 바탕으로 위의 조직도와 분류도를 살펴보자

 

조직도일 경우 

  • 아버지는 할아버지다.
  • 아들은 아버지이다. 
  • 딸은 아버지이다.
  • 아버지 영희아빠 = new 딸(); // 딸을 낳으니 아버지 역활을 하는 영희아빠라 이름 지었다?

분류도일 경우

  • 포유류는 동물이다. 
  • 고래는 포유류이다. 
  • 고래는 동물이다. 
  • 동물 뽀로로 = new 펭귄(); //펭귄을 낳으니 동물 역활을 하는 뽀로로라 이름 지었다

자바 언어에서 inheritance(상속)라는 키워드는 존재하지 않고 extends(확장)가 존재한다. 


객체 참조 변수명은 객체스럽게, 클래스명은 클래스명답게 정하는 습관을 들이자. 

객체 참조 변수명은 결국 객체를 참조하게 되는데 객체의 특성은 유일무이한 것이다. 

클래스명은 분류스럽게, 객체 참조 변수명은 유일무이한 사물처럼 작명해야 한다는 말이다. 

  • 조류 bird = new 조류();

조류의 나이는? 답할 수 있겠는가? 그래서 클래스이다. 

bird의 나이는? 답할 수 있겠는가? 그래서 클래스이다. 

 

bird는 논리적으로 맞지 않다. 프로그램적으로 허용된느 코드이긴 하지만 인간의 논리에 맞지 않는다는 것이다. 

이런 작은 논리들이 모여 결국 큰 프로그램을 만들게 되는데 그 논리들이 인간적이면 더 좋지 않겠는가?

바로 그게 객체 지향인 것이다. 


상위 클래스에서만 ***() 메서드를 구현했지만 하위 클래스의 객체에서 ***()  메서드를 사용할 수 있다.

상속한다는 것이 이렇게 상위 클래스의 특성을 상속한다는 의미이지 부모-자식 관계는 아니다.

절차적/구조적 프로그래밍 입장에서 생각해보면 하위 클래스에서 ***()메서드를 다시 작성하지 않아도 된다는 것,

재사용할 수 있다는 것은 정말 감동적일 것이다. 

상속의 장점은 코드의 중복성이 제거됐다.

 

또한 상속의 장점에는 다형적 표현이 가능하다는 다형성이 있는데 이것은 다음 객체지향 특성 다형성에서 살펴보자.


상속에 대한 또 하나의 오해를 짚고 넘어가자

상속은 is a 관계를 만족해야 한다는 말을 들어 본 적이 있을 것이다. 

 

is a 관계는 객체(클래스의 인스턴스)와 클래스의 관계로 오해될 소지가 많다. 

  • 객체 is a 클래스
  • 박지성 is a 사람 -> 박지성은 한 명의 사람이다.
  • 뽀로로 is a 펭귄 -> 뽀로로는 한 마리의 펭귄이다.

그렇다면 상속 관계의 더 명확한 영어 표현은 무엇일까? 바로 is a kind of 관계이다.

  • 하위 클래스 is a kind of 상위 클래스
  • 펭귄 is a kind of 동물 -> 펭귄은 동물의 한 분류다.

다음을 꼭 기억하자

  • 객체 지향의 상속은 상위 클래스의 특성을 재사용하는 것이다.
  • 객체 지향의 상속은 상위 클래스의 특성을 확장하는 것이다. 
  • 객체 지향의 상속은 is a kind of 관계를 만족해야한다. 

왜 자바는 다중 상속을 지원하지 않을까?

 

인어공주를 생각 해보자 인어는 사람과 물고기를 상속한다고 생각해보자

사람도 수영을 할 수 있고, 물고기도 수영할 수 있는데 인어에게 수영을 한다고 시키면 팔과 다리를 저어 수영해야 할까?

가슴,등,꼬리 지느러미로 헤엄쳐야할까?

바로 모호성이 발생한다. 따라서 자바에서는 인터페이스를 도입해 다중 상속의 득은 취하고 실은 과감히 버렸다.

 

상속 관계가 is a kind of 관계라고 앞에서 설명했다.

  • 상속관계 : 하위 클래스 is a kind of 상위 클래스
  • 해석: 하위 클래스는 상위 클래스의 한 분류이다.
  • 예제: 펭귄는 동물의 한 분류이다.

인터페이스는 상속과는 다르게 쓰는 것이 유용하다는 결론에 도달한다.

  • 인터페이스 : 구현 클래스 is able to 인터페이스
  • 해석: 구현 클래스는 인터페이스 할 수 있다.
  • 예제: 펭귄는 헤엄칠 수 있다.

인터페이스는 be able to, "무엇을 할 수 있는" 이라는 표현 형태로 만드는 것이 좋다.  

상위 클래스는 하위 클래스에게 특성(속성,메소드)을 상속해 주고, 인터페이스는 클래스가 '"무엇을 할 수 있다"라고 기능을 구현하도록 강제한다. 

 

상위클래스는 물려줄 특성이 풍성할수록 좋다. -> LSP(리스코프 치환 원칙)

인터페이스는 구현을 강제할 메서드의 개수가 적을수록 좋다는 결론에 도달할 수 있다.  -> ISP(인터페이스 분학 원칙)


public class Driver {
	public static void main(String[] args) {
		Penguin pororo = new Penguin();

		pororo.name = "뽀로로";
		pororo.habitat = "남극";

		pororo.showName();
		pororo.showHabitat();

		Animal pingu = new Penguin();

		pingu.name = "핑구";
		// pingu.habitat = "EBS";

		pingu.showName();
		// pingu.showHabitat();

		// Penguin happyfeet = new Animal();
	}
}

 

Penguin 클래스의 인스턴스만 힙 영역에 생긴 게 아니라 Animal 클래스의 인스턴스도 함께 힙 영역에 생긴 것을 알 수 있다. 하위 클래스의 인스턴스가 생성될 때 상위 클래스의 인스턴스도 함께 생성된다.

JVM이 하위 클래스의 객체를 생성될 때 가장 먼저 상위 클래스의 객체를 생성한다.

이후 자식 클래스에서 추가한 필드와 메서드가 객체에 추가됨으로써 하위 클래스의 객체가 완성되는 것이다.

또한 모든 클래스의 최상위 클래스인 Object 클래스의 인스턴스도 함께 생성되는 것이다. 

 

pinggu 객체 참조 변수는 사실 펭귄이면서 자신이 펭귄인지 모르고 동물이라는 것만 인식하고 있다. 

따라서 pinggu 객체 참조 변수는 펭귄 클래스의 속성과 메서드를 사용할 수 없다. 

그 이유는 형변환, 명식적 형변환 연산(Casting)과 암묵적 형변환(Promotion)에 답이 있다. 

 

📖Reference

  • 도서 '스프링 입문을 위한 자바 객체 지향의 원리와 이해'

댓글