자바의 정석 Chapter 07 객체 지향 프로그래밍 Ⅱ - 01. 상속편

강철의사나이·2021년 12월 19일
0
post-thumbnail

1. 상속 (inheritance)

1.1 상속의 정의와 장점

상속이란 기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것이다.

상속을 통해 코드를 작성할 때 이점

  1. 보다 적은 양의 코드로 새로운 클래스를 작성
  2. 코드를 공통적으로 관리하여 코드의 추가 및 변경이 용이
  3. 코드의 재사용성을 높임
  4. 코드의 중복을 제거해서 프로그램의 생산성과 유지보수에 크게 기여함
class Parent {}
class Child extends Parent {
	  // ...
}

이 두 클래스는 서로 상속 관계에 있다고 하며
상속해 주는 클래스를 '조상 클래스' (Parent)
상속 받는 클래스를 '자손 클래스' 라고 한다. (Child)

서로 상속관계에 있는 두 클래스를 아래와 같은 용어로도 사용한다.

  • 조상 클래스 : 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스
  • 자손 클래스 : 자식(child)클래스, 하위(sub) 클래스, 파생된(derived)클래스

자손 클래스는 조상 클래스의 모든 멤버를 상속받기 때문에
Child 클래스는 Parent 클래스의 멤버들을 포함한다고 할 수 있다.

만일 Parent 클래스에 age라는 정수형 변수를 멤버변수로 추가하면
자손 클래스는 조상의 멤버를 모두 상속받기 때문에
Child 클래스는 자동적으로 age라는 멤버 변수가 추가된 것과 같은 효과를 받는다.

class Parent {
	int age;
}
class Child extends Parent { }

반대로 자손인 Child 클래스에 새로운 멤버로 play() 메서드를 추가해보자.

class Parent {
	int age;
}
class Child extends Parent {
	void play () {
    	System.out.println("놀자");
    }
}

Child 클래스에 새로운 코드가 추가되도 조상인 Parent 클래스는 아무런 영향을 받지 않는다.

  • 조상 클래스가 변경되면 자손 클래스는 자동으로 영향을 받지만
  • 자손 클래스가 변경되도 조상 클래스엔 아무런 영향을 주지 못한다.

자손 클래스는 조상 클래스의 모든 멤버를 상속 받으므로
항상 조상 클래스보다 같거나 많은 멤버를 갖는다.

상속에 상속을 거듭할수록 상속받는 클래스의 멤버 개수는 점점 늘어난다.

따라서 상속을 받는다는 것은 조상 클래스를 확장(extend)한다는 의미로 해석할 수도 있고
이것이 상속에 사용되는 'extend'를 쓰는 이유다.

  • 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
  • 자손 클래스의 멤버 개수 >= 조상 클래스의 멤버 개수

! 접근 제어자 (access modifier)가 private 또는 dafault인 멤버들은
상속되지 않는다기보다 상속은 받지만 자손 클래스로부터의 접근이 제한된다.

ex) 공통 부모 클래스를 둔 Child2

class Parent {}
class Child extends Parent { }
class Child2 extends Parent { }

클래스 Child와 Child2가 모두 Parent 클래스를 상속받고 있지만
Child와 Child2간에는 아무런 관계가 성립하지 않는다.
클래스 간의 관계는 형제관계와 같은 것은 없다.

Child 클래스와 Child2 클래스에 공통적으로 추가해야 할 멤버가 있을 경우
공통 조상인 Parent 클래스에만 추가해주면 된다.

! 이렇듯 코드를 한 곳에서 관리함으로써 코드의 중복이 줄어든다.

ex) Child 클래스로부터 상속받는 GrandChild 클래스

class Parent {}
class Child extends Parent {}
class Child2 extends Parent {}
class GrandChild extends Child {}

GrandChild 클래스는 Child 클래스의 모든 멤버,
Child 클래스의 조상인 Parent 클래스로부터 상속받은 멤버까지 상속받게 된다.

Parent 클래스는 GrandChild의 간접 조상
Child 클래스는 GrandChild의 직접 조상이 된다.

ex) Parent 클래스에 정수형 변수 age 추가

class Parent {
	int age;
}
class Child extends Parent {}
class Child2 extends Parent {}
class Child extends Child {}

Parent 클래스는 Child, Child2, GrandChild 클래스의 조상이므로
추가된 age 변수는 Child, Child2, GrandChild 클래스 모두에 추가된다.
제거한 경우도 마찬가지로 모두 지워진다.

자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인서턴스로 생성된다.

1.2 클래스 간의 관계 - 포함관계

상속이외에도 클래스를 재사용하는 또 다른 방법이 있다.
바로 크래스간에 '포함(composite)'관계를 맺어 주는 것이다.
클래스 간의 포함관계를 맺어 주는 것은 한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것이다.

원(Circle)을 표현하기 위해 Circle이라는 클래스를 작성한다.

class Circle {
	int x; // 원점의 x좌표
    int y; // 원점의 y좌표
    int r; // 반지름
}

그리고 좌표상의 한 점을 다루기 위한 Point클래스를 작성한다.

class Point{
	int x; // x좌표
    int y; // y좌표
}

Point 클래스를 재사용해서 Circle클래스를 작성하면 다음와 같이 작성할 수 있다.

class Circle {
	Point c = new Point() ; //원점
    int r;
}

이처럼 한 클래스를 작성하는 데 다른 클래스를 멤버변수로 선언하여 포함시키면
하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음,
이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있고
작성된 단위 클래스들은 다른 클래스를 작성하는데 재사용할 수 있다.

그리고 단위 클래스 별로 코드가 작게 나뉘어 작성되기 때문에 코드를 관리하기도 보다 쉽다.

1.3 클래스간의 관계 결정하기

클래스를 작성하는데 상속관계를 맺어 줄 지 포함관계를 맺어 줄 지 결정하기 혼돈스러울 수 있다.

Circle 클래스의 경우, Point 클래스를 포함시키는 대신 상속관계를 맺어보자.

class Circle {
	Point c = new Point() ; //원점
    int r;
}

=>

class Circle extends Point{
	int r;
}

두 경우를 보면 상속이나 포함관계나 별 차이가 결과적으로 없어보인다.

그럴 때는 '~은 ~이다 (is a)'와
'~은 ~을 갖고 있다 (has a)'를 넣어서 문장을 만들어 보면 클래스 간의 관계를 명확하게 파악할 수 있다.

  • 원(Circle)은 점(Point)이다. - Circle is a Point.
  • 원(Circle)은 점(Point)을 갖고 있다. - Circle has a Point.

원은 원점(Point)과 반지름으로 구성되므로
두 문장 중 두 번째 문장이 더 옳다는 것을 알 수 있다.

이처럼 클래스를갖고 문장을 만들었을 때 '~은~이다.' 라는 문장이 성립하면 상속관계로 맺고
'~은~을 갖고 있다.' 는 문장이 성립하면 포함관계를 맺어준다.

그래서 Circle클래스와 Point클래스 간의 관계는 상속관계 보다 포함관계를 맺어주는 것이 더 옳다.

클래스 간의 관계를 결정하는 것이 매번 이렇게 딱 떨어지는 것은 아니지만
적어도 클래스간의 관계를 맺어주는데 필요한 가장 기본적인 원칙에 대한 감은 잡을 수 있을 것이다.

1.4 단일 상속 (Single inheritance)

다른 객체지향언어인 C++에서는 여러 조상 클래스로부터 상속받는 것이 가능한 '다중상속(multiple inheritance)를 허용하지만
자바에서는 오직 단일 상속만을 허용한다.
따라서 자바에서는 둘 이상의 클래스로부터 상속을 받을 수 없다.

class TVCE extends TV, VCR { // 에러. 자바에서 조상 클래스는 단 하나만 허용된다.
	//...
}

장점 : 여러 클래스로부터 상속을 받아 복합적인 기능을 가진 클래스를 쉽게 작성 가능

단점 : 클래스간의 관계가 매우 복잡해지고, 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같을 경우 구별할 수 없음

1.5 Object클래스 - 모든 클래스의 조상

Object 클래스는 모든 클래스 상속계층도의 최상위에 있는 조상 클래스다.
다른 클래스로부터 상속 받지 않는 모든 클래스들도 자동적으로 Object클래스로부터 상속받는다.

아무런 상속도 받지 않는 TV 클래스

class Tv{
	...
}

위 코드를 컴파일 하면 컴파일러는 위의 코드를 다음과 같이 자동적으로
'extends Object'를 추가해서 TV 클래스가 Object로부터 상속받도록 한다.

class Tv extends Object {
	...
}

이렇게 해서 Object 클래스가 모든 클래스의 조상이 되도록 한다.
다른 클래스로부터 상속을 받더라도 상속 계층도를 따라 올라가서 조상 클래스의 조상클래스 위에는
결국 마지막에 최상위 조상 클래스는 Object클래스가 있다.

그래서 자바의 모든 클래스들은 Object클래스의 멤버들을 상속 받기 때문에
Object 클래스에 정의된 멤버들을 사용할 수 있다.

그동안 toString()이나 equals(Object )와 같은 메서드를 따로 정의하지 않아도 사용할 수 있었던 이유는
이 메서드들이 Object 클래스에 정의된 것들이기 때문이다.

0개의 댓글