상속의 정의와 장점

기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다. 보다 적은 양의 코드로 새로운 클래스를 만들 수 있음과 동시에 코드의 추가 및 변경이 용이하다.

자바에서 상속을 구현하는 방법은 클래스 이름뒤에 상속받고자 하는 클래스의 이름을 키워드 'extends'와 함께 써 주기만 하면 된다.

class Child extends Parent {
	// ...
}

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

서로 상속관계에 있는 두 클래스를 조상 클래스, 자손 클래스와 같은 용어로 표현하기도 한다.

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

아래와 같이 서로 상속관계에 있는 두 클래스를 표현하면 다음과 같다.

class Parent { }
class Child extends Parent { }

클래스는 타원으로 표현했고 클래스간의 관계는 화살표로 표시했다. 

이와 같이 클래스 간의 상속관계를 그림으로 표현한 것을 상속계층도(class hierarchy)라고 한다.

자손 클래스는 조상의 멤버를 모두 상속받는다.

 

조상 클래스가 변경되면 자손 클래스는 자동적으로 영향을 받게 되지만, 자손 클래스가 변경되는 것은 조상 클래스에 아무런 영향을 주지 못한다.

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

- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

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

또한 같은 조상클래스를 가지고 있더라도 자손 클래스끼리는 아무런 관계도 성립되지 않는다.

상속을 이용하는 이유는 같은 내용의 코드를 하나 이상의 클래스에 중복적으로 추가해야하는경우에는 상속관계를 이용해서 코드의 중복을 최소화해야 한다. 자손 클래스는 조상 클래스의 모든 멤버를 물려받으므로 조상의 조상 클래스까지 상속받게 된다.

 

 

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

상속 이외에도 클래스를 재사용하는 또 다른 방법이 있는데, 그것은 클래스 간에 포함(Composite) 관계를 맺어 주는 것이다.

클래스 간의 포함관계를 맺어 주는 것은 한 클래스의 멤버변수로 다른 클래스 타입의 참조 변수를 선언하는 것을 뜻한다.

class Circle {
	int x;
	int y;
	int r;
}
class Point {
	int x;
	int y;
}

Point 클래스를 이용해서 Circle클래스를 작성한다면

class Circle { 
	Point c = new Point();
	irt r;
}

이와같이 한 클래스를 작성하는 데 다른 클래스를 멤버 변수로 선언하여 포함시키는 것은 코드를 다시 작성할 수고를 줄이고 작은 단위로 분리함으로써 재사용 가능성도 높아진다.

그리고 무엇보다 코드가 간결해서 이해하기 쉽다.

 

클래스간의 관계 결정하기

클래스를 작성하는 데 있어서 상속관계를 맺어 줄 것인지 포함관계를 맺어 줄 것인지 판단하는 방법은 ~은~이다(is-a)와 ~은~을 가지고 있다.(has-a)를 넣어서 문장을 만들어보면 된다.

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

이렇게 비교를해보면 두 번째 문장이 맞다는 것을 더 쉽게 알 수 있다.

원과 점 사이의 관계는 상속보다는 포함 관계가 어울린다는 사실을 알 수 있다.

상속관계 '~은 ~이다.(is-a)'
포함관계 '~은~을 가지고 있다.(has-a)'

프로그램의 모든 클래스를 분석하여 가능한 많은 관계를 맺도록 노력해서 코드의 재사용성을 높여야 한다.

public class DrawShape {
	public static void main(String[] args) {
		Point[] p = {	
				new Point(100, 100),
				new Point(140, 50),
				new Point(200, 100)
		};
		Triangle t = new Triangle(p);
		Circle c = new Circle(new Point(150, 150), 50);
		
		t.draw();
		c.draw();
	}

}

class Shape {
	String color = "black";
	void draw() {
		System.out.printf("[color=%s]%n", color);
	}
}

class Point {
	int x;
	int y;

	Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	Point() {
		this(0, 0);
	}

	String getXY() {
		return "(" + x + "," + y + ")"; // x와 y의 값을 문자열로 반환
	}
}

class Circle extends Shape {
	Point center;
	int r;

	Circle() {
		this(new Point(0, 0), 100);
}
	Circle(Point center, int r) {
		this.center = center;
		this.r = r;
	}
	
	void draw() {
		System.out.printf("[center=(%d, %d), r=%d, color=%s]%n", center.x, center.y, r, color);
	}
}

class Triangle extends Shape {
	Point[] p = new Point[3];
	
	Triangle(Point[] p) {
		this.p = p;
	}
	
	void draw() {
		System.out.printf("[p1=%s, p2=%s, p3=%s, color=%s]%n",
				p[0].getXY(),p[1].getXY(), p[2].getXY(), color);
	}
}
[p1=(100,100), p2=(140,50), p3=(200,100), color=black]
[center=(150, 150), r=50, color=black]

도형을 의미하는 shape클래스를 정의하고, 2차원 좌표에서 점을 의미하는 Point클래스를 정의한 다음, 이 두 클래스를 재활용해서 Circle클래스와 Triangle클래스를 정의하였다.

Circle클래스는 Shape클래스로부터 모든 멤버를 상속받았으므로, Shape클래스에 정의된 color이나 draw()를 사용할 수 있다.

 

 

class Shape {
		String color = "black";

		void draw() {
			System.out.printf("[color=%s]%n", color);
		}
}

Circle클래스에도 draw()가 정의되어 있다. 그러면 둘 중에 어떤 것이 호출되는 것인가?

그것은 Circle클래스의 draw()가 호출된다.

class Circle extends Shape {
		...
		void draw() {
			System.out.printf("[center=(%d, %d), r=%d,color=%s]%n",
					center.x, center.y, r, color
		}
}

이처럼 조상 클래스에 저의 된 메서드와 같은 메서드를 자손 클래스에 정의하는 것을'오버 라이딩'이라고 하며 매우 중요한 내용이다.

Circle c = new Circle(new Point(150, 150), 50);

위의 문장은 아래 두 문장을 합쳐 놓은 것이다.

Point P = new Point(150, 150);
Circle c = new Circle(P, 50);

문장이 복잡하면 여러 문장으로 분해하는 연습을 하면 이해하는데 한결 도움이 된다.

 

 

단일 상속(single inheritance)

자바에서는 오직 단일 상속만을 허용한다.

class TVCR extends TV, VCR {	// 에러. 조상은 하나만 허용한다
	//...
}

만약 이대로 한다면 Multiple markers at this line이라는 에러가 난다.

class Tv {
	boolean power;
	int channel;
	
	void power()	{ power = !power; }
	void channelUP() { ++channel; }
	void channelDown() { --channel; }
}

class VCR {
	boolean power;
	int counter = 0;
	void power() {	power = !power; }
		void play() { }
		void stop() { }
		void rew() { }
		void ff() { }
	}

class TVCR extends Tv {
	VCR vcr = new VCR();
	
	void play() {
		vcr.play();
	}
	
	void stop() {
		vcr.stop();
	}
	void rew() {
		vcr.rew();
	}
	void ff() {
		vcr.ff();
	}

}

자바는 다중 상속을 허용하지 않으므로 Tv클래스를 조상으로 하고, VCR클래스는 TVCR클래스에 포함시켰다. 그리고 TVCR클래스에 VCR클래스의 메서드와 일치하는 선언부를 가진 메서드를 선언하고 내용은 VCR클래스의 것을 호출해서 사용하도록 했다.

외부적으로는 TVCR클래스의 인스턴스를 사용하는 것처럼 보이지만 내부적으로는 VCR클래스의 인스턴스를 생성해서 사용하는 것이다.

 

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

모든 클래스 상속계층도의 최상위에 있는 조상 클래스이다. 다른 클래스로부터 상속받지 않는 모든 클래스들은 Object클래스로부터 상속받게 함으로써 이것을 가능하게 한다.

class Tv {
	...
}
class Tv extends Object {
	...
}

모든 상속계층도의 최상위에는 Object클래스가 위치한다.

 

+ Recent posts