기존클래스를 재사용하여 새로운 클래스를 작성하는 것
두 클래스를 조상과 자손의 관계로 맺어주는 것
자손은 조상의 모든 멤버(속성&기능)를 상속받는다.
각 클래스의 생성자에는 자신에 정의된 멤버변수만을
초기화하는 코드와 조상의 생성자를 호출하는 코드를 넣어서
상속받은 멤버변수들은 조상의 생성자에 의해 초기화되도록 한다.
자손의 멤버개수는 조상보다 적을수 없다.(같거나 많다.)
상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로
새로운 클래스를 작성할 수 있게 된다.
코드를 공통적으로 관리할 수 있기 때문에
코드의 추가 및 변경이 매우 유용해진다.
상속받을 클래스 이름 extends 새로 정의할 클래스 이름public class Point{
int x, y;
}
public class Point3D extends Point{
int z;
}
2차원 좌표의 한 점을 표현하기 위한 Point클래스가 정의되어 있을 때
3차원 좌표의 한 점을 표현하기 위한 Point3D클래스를
기존의 Point클래스로부터 상속받아서 정의했다.
자손클래스는 조상클래스에 정의된 멤버들을 그대로 상속받기 때문에 Point3D클래스는 조상인 Point클래스에 정의된
멤버변수 x와 y를 모두 상속받게 된다.
그래서 Point3D클래스에는 새로운 멤버변수 z만 정의하면 되는 것이다.
-> 자손 클래스에는 추가적인 부분만 정의하면 되는 것
각 클래스의 멤버를 다이어그램으로 그렸다.
항상 자손인 Point3D클래스가 조상인 Point클래스를 완전히 포함하고 있다.
만일 Point3D클래스를 조상으로 하는 다른 클래스를 정의한다면
이 두 클래스의 모든 멤버를 포함하는 보다 확장된 형태가 될 것이다.
공통부분은 조상클래스에서 관리하고 개별적인 부분은 자손클래스에서 관리
조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 영향이 없다.
public class Parent(){}
public class Child extends Parent{}
public class Child2 extends Parent{}
public class GrandChild extends Child{}
이와 같이 4개의 클래스가 정의되어 있고
이 클래스들의 상속관계를 간략한 그림으로 그렸다.(상속계층도)
-> 상속계층도를 그리면 클래스간의 관계를 쉽게 이해할 수 있다.
상속관계에는 조상과 자식관계만 있을 뿐, 형제와 같은 관계는 없다.
Child와 Child2클래스는 Parent클래스라는 공통 조상을 가지고 있지만
이 두 클래스는 아무런 관계도 없다.
만약 Child와 Child2클래스가 새로운 메서드를 추가해야 한다면
이 두 클래스에 각각 추가하는 것보다 이들의 공통조상인
Parent클래스에 추가하는 것이 좋다.
public class Parent(int x;){}
public class Child extends Parent{}
public class Child2 extends Parent{}
public class GrandChild extends Child{}
위와 같이 Parent클래스에 새로운 멤버변수 x가 추가되면
Parent의 모든 자손클래스들 Child, Child2, GrandChild에
멤버변수 x가 새로 추가되는 것이다.
이처럼 조상클래스의 변경은 모든 자손클래스와
자손의 자손클래스까지 영향을 미치지만
자손의 변경은 조상에게 아무런 영향을 미치지 않는다.
public class Parent(int x;){}
public class Child extends Parent{int y;}
public class Child2 extends Parent{}
public class GrandChild extends Child{}
위와 같이 Child클래스에 새로운 멤버변수 y가 추가되었다면
Child클래스의 조상인 Parent클래스는 아무런 영향을 받지 않는다.
Child클래스의 자손인 GrandChild클래스만 영향을 받는다.
한 클래스의 멤버변수로 다른 클래스를 선언하는 것
작은 단위의 클래스를 먼저 만들고, 이들을 조합해서 하나의
커다란 클래스를 만든다.
public class Car{
// 엔진
Engine engine = new Engine();
// 문
Door door = new Door();
}
자동차를 만들 때 자동차를 구성하는 엔진이나 문과 같은 주요부분을
먼저 만들어 놓고 이들을 조립하는 것처럼
Car클래스를 정의할 때도 Engine과 Door클래스를 미리 정의해 놓고
이들을 포함시키도록 하는 것이 좋다.
가능한 많은 관계를 맺어주어 재사용성을 높이고 관리하기 쉽게 한다.
상속관계와 포함관계를 가지고 문장을 만들어 본다.
클래스간의 관계를 상속으로 할 것인지 포함으로 할 것인지 결정하는
가장 간단한 방법은 is-a와 has-a를 가지고 문장을 만들어보는 방법이다.
Dog클래스와 Animal클래스를 가지고 문장을 만들었다.
자연스러운 문장은 Dog is a Animal(개는 동물이다.)이 된다.
그래서 같이 Dog클래스가 Animal클래스를 포함하는 것보다
Animal클래스를 상속받도록 하는 것이 더 적절하다.
public class Animal{}
public class Dog extends Animal{}
실제 프로그래밍을 하다 보면 대부분의 경우 포함관계고,
기존의 클래스에 새로운 기능이 추가된 새로운 클래스를 만들 때는
상속관계를 맺어주면 된다.
코드를 재사용함으로써 서로 유기적으로 연결되도록 한다.
유기적으로 연결하여 한 곳의 변경이 다른 곳에
자동적으로 영향을 미치도록 하여 작업의 양을 줄여주고,
오류의 가능성을 낮춘다.
상속관계는 포함관계보다 객체지향개념적으로 좀더 중요한 의미를 갖는다.
다형성과도 깊은 관계가 있다.
상속계층도에 포함된다.
조상의 변화에 따라 모든 자손들이 영향을 받기 때문에
상속관계를 잘못 맺어주면 원하지 않는 영향을 줄 수도 받을 수도 있다.
원(Circle)은 도형(Shape)이다.(A Circle is a Shape.) : 상속관계
원(Circle)은 점(Point)를 가지고 있다.(A Circle has a Point.) : 포함관계
public class Shape {
String color = "Blue";
public void draw() {
System.out.println("도형을 그린다.");
}
}
public class Point {
int x, y;
public Point() {
this(0, 0);
}
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
Shape클래스와 Point클래스를 정의하고,
이들을 이용해서 Circle를 정의했다.
public class Circle extends Shape{
Point center;
int r;
public Circle() {
this(new Point(0, 0), 100);
}
public Circle(Point center, int r) {
this.center = center;
this.r = r;
}
public static void main(String[] args) {
Circle circle01 = new Circle();
Circle circle02 = new Circle(new Point(150, 150), 50);
}
}
new에 의해서 Circle클래스타입에 인스턴스를 생성한다.
그리고 Circle클래스의 기본 생성자를 호출한다.
기본생성자는 매개변수가 있는 다른 생성자를 호출한다.
호출된 생성자의 매개변수에
Point클래스의 인스턴스(new Point(0, 0))와, 100을 전달한다.
// Circle클래스의 기본생성자
public Circle() {
// 매개변수가 있는 생성자 호출
// 호출된 생성자에 매개변수에
// Point클래스의 인스턴스(new Point(0, 0))와, 100을 전달
this(new Point(0, 0), 100);
}
// 기본생서자에서 호출한 생성자
// 기본생성자에서 전달한 값들을 매개변수들이 받는다.
// Point center = new Point(0, 0);
// int r = 100;
public Circle(Point center, int r) {
// 참조변수 this를 이용해 인스턴스 초기화 진행
this.center = center; // center = new Point(0, 0);
this.r = r; // r = 100;
}
Circle클래스의 멤버변수 center는 생성된 인스턴스(new Point(0, 0))
의 메모리 주소를 참조하게 된다. 인스턴스는 Point클래스에서
매개변수를 가진 생성자를 호출한다.
호출된 생성자는 매개변수들이 전달받은 값(0, 0)을 가지고
인스턴스 초기화를 진행한다.
public class Point {
int x, y; // int x = 0; / int y = 0;
public Point() {
this(0, 0);
}
// 생성자 호출됨 int x = 0, int y = 0
public Point(int x, int y) {
// 참조변수 this를 이용해 인스턴스 초기화 진행
this.x = x; // x = 0;
this.y = y; // y = 0;
}
}
// 호출된 생성자
// 매개변수들은 다음과 같은 값을 전달받는다.
// Point center = new Point(150, 150)
// int r = 50
public Circle(Point center, int r) {
// 전달받은 값으로 인스턴스 초기화 진행
this.center = center;
this.r = r;
}
public class Point {
int x, y;
public Point() {
this(0, 0);
}
// 생성자 호출됨
// 각 매개변수는 다음과 같은 값을 전달받는다.
// int x = 150 / int y = 150
public Point(int x, int y) {
// 인스턴스 초기화 진행
this.x = x;
this.y = y;
}
}
public class Point {
int x, y;
public Point() {
this(0, 0);
}
// 생성자 호출됨
// 매개변수들은 인스턴스가 생성되면서 전달한 값을 전달받는다.
public Point(int x, int y) {
// 인스턴스 초기화 수행
this.x = x;
this.y = y;
}
}
new를 이용해서Triangle클래스타입의 인스턴스를 생성한다.
Triangle클래스에 매개변수가 있는 생성자를 호출한다.
public class Triangle extends Shape{
Point[] p;
// 호출된 생성자
// 매개변수는 p가 참조하고있는 인스턴스 메모리주소를 받는다.
public Triangle(Point[] p) {
// 인스턴스 초기화 진행
this.p = p;
}
public Triangle(Point p1, Point p2, Point p3) {
p = new Point[] {p1, p2, p3};
}
public static void main(String[] args) {
Triangle triangle01 = new Triangle(p);
}
}
단 하나의 조상클래스로부터만 상속받을 수 있는 것.
Java는 단일상속만을 허용한다.
다중상속을 허용하면 여러 클래스로부터 상속받을 수 있기 때문에
더 쉽게 클래스를 정의할 수 있지만 조상이 많아질 수록
상속계층도가 너무 복잡해져서 클래스간의 관계를 관리하기 어려워진다.
서로 다른 조상으로부터 같은 이름의 멤버를 상속받는 경우
충돌의 문제도 있다.
비중이 높은 클래스 : 상속관계를 이용한다.
나머지 클래스 : 포함관계를 이용한다.
public class Tv {
// 전원상태(on / off)
boolean power;
// 채널
int channel;
// 전원을 on / off시키는 메서드
public void power() {
power =! power;
}
// 채널이동(+) 메서드
public void channelUp() {
++channel;
}
// 채널이동(-) 메서드
public void channelDown() {
--channel;
}
}
public class Netflix {
// 시청할 프로그램 이름
String programName;
public Netflix(String programName) {
this.programName = programName;
}
public void play() {}
public void stop() {}
}
public class SmartTv extends Tv {
Netflix netflix = new Netflix("Bridgerton");
String programName = netflix.programName;
public void play() {
netflix.play();
}
public void stop() {
netflix.stop();
}
}
TV클래스를 상속받았다.
Netflix클래스는 포함관계를 사용하여 Netflix인스턴스를 사용한다.
Netflix클래스에 정의된 멤버들을 SmartTv클래스에 똑같이 정의하고
메서드의 경우 코드를 작성할 필요없이 포함시킨
Netflix인스턴스의 메서드를 호출하기만 하면된다.