본문 바로가기
언어/자바의 정석

[자바의 정석] Chapter 7 객체 지향 프로그래밍II(1) - 상속

by chan10 2021. 2. 16.

상속의 정의와 장점

o  기존의 클래스를 재사용하여 새로운 클래스는 작성하는 것을 상속이라 한다. 상속을 사용하여 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 사용하기에 코드의 추가 및 변경이 용이하다.

o  코드의 재사용성을 높이고 중복을 제거하여 생산성과 유지보수에 기여한다.

o  상속을 하는 방법은 새로 작성하는 클래스 이름 뒤에 ‘extends’키워드와 상속받는 클래스의 이름을 적어주면 된다.

class Child extends Parent {
    // Parent클래스를 상속받음
}

o  상속해주는 클래스를 조상 클래스라고 하고 상속받는 클래스를 자손 클래스라고 한다. 클래스 관계는 부모 자식 관계만 있으며 형제 관계는 존재하지 않는다.

조상 클래스 : 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스

자손 클래스 : 자식(child)클래스, 하위(sub)클래스, 파생된(derived)클래스

o  프로그램이 커질수록 클래스 간의 관계가 복잡 해지는데 클래스 간의 상속 관계를 표현한 그림상속계층도라고 한다.

 

o  자손 클래스는 조상 클래스의 모든 멤버 변수를 상속받기에 조상 클래스에서 변경사항이 생길 경우 모든 자손 클래스가 영향을 받지만 반대로 자손 클래스에서 변경되는 것은 조상 클래스에 영향을 주지 못한다.

- 생성자와 초기화 블록은 상속되지 않는다. 멤버(멤버 변수, 메서드)만 상속된다.

- 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

o  GrandChild클래스는 Child클래스를 상속받으면서 Child Parent로부터 상속받은 멤버까지 상속받는다. 따라서 Parent클래스에 멤버 변수가 추가 및 제거가 되면 모든 자손에 추가 및 제거가 되기에 GrandChild에게도 영향이 있다. 그렇기에 상속관계를 맺어주면 자손 클래스의 공통적인 부분은 조상 클래스에서 관리하고 자손 클래스는 정의된 멤버만 관리하면 되기에 관리가 더 쉬워진다.

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

class Parent{
    int age;
}
//Parent클래스를 상속 받음
class Child extends Parent { }
class Child2 extends Parent { }
class GrandChild extends Child { }
class Tv{
    boolean power;    // 전원 on/off
    int channel;    // 채널 조정
    
    void power() { power=!power;}
    void channelUp() {channel++;}
    void channelDown() {channel--;}
}
//  Tv클래스를 상속받음
class CaptionTv extends Tv {
    boolean caption;    // 자막 on/off
    void displayCaption (String text) {
        if(caption ) {
            System.out.println(text);
        }
    }
}
 
public class Test {    
    public static void main(String[] args) {
        CaptionTv ctv = new CaptionTv();
        ctv.channel = 10// 상속받은 멤버
        ctv.channelUp();    // 상속받은 멤버
        System.out.println(ctv.channel);
        ctv.displayCaption("Hello, World");
        ctv.caption = true;    // 자막을 켠다
        ctv.displayCaption("Hello, World");
    }
}

 

클래스 간의 관계 포함관계

o  클래스 간의 관계를 맺을 때 상속이외에 포함(Composite)관계를 맺어주는 방법도 있다. 포함관계를 맺어주는 것은 클래스의 멤버 변수로 다른 클래스 타입의 참조변수를 선언하는 것을 뜻한다.

o  하나의 거대한 클래스를 작성하는 것 보다 단위별로 여러 개의 클래스를 작성한 다음, 포함관계로 재사용하면 간결하고 손쉽게 클래스를 작성할 수 있다.

class Circle{
    int x;
    int y;
    int r;
}
 
class Point{
    int x;
    int y;
}
class Circle{
    Point p = new Point();
    int r;
}
 
class Point{
    int x;
    int y;
}

 

클래스 간의 관계 결정하기

o  클래스를 작성할 때 상속 관계를 맺을지 포함 관계를 맺을지 결정하기 어려울 때는 문장을 만들어보면 클래스 간의 관계를 명확하게 할 수 있다.

(Circle)은 점(Point)이다. – Circle is a Point

(Circle)은 점(Point)을 가지고 있다. – Circle has a Point.        <- O

o  이처럼 클래스 간의 관계를 맺을 때 ‘~~이다라는 문장이 성립한다면 상속관계, ‘~~을 가지고있다문장이 성립한다면 포함관계를 맺어준다.

o  ex) [SuperCarCar이다] -> 상속관계, [DeckCard를 가지고 있다] -> 포함 관계

상속 관계 ‘~~이다.(is-a)’

포함관계 “~~을 가지고 있다.(has-a)’

 

예제)

class Shape{    // 도형을 의미하는 클래스 (조상 클래스)
    String color = "black";
    void draw() {
        System.out.printf("[color=%s]%n",color);
    }
}
 
class Point{    // 점을 의미하는 클래스 (포함관계 클래스)
    int x;    // 2차원 좌표의 점 위치
    int y;
    Point(int x, int y){
        this.x=x;
        this.y=y;
    }
    
    Point(){
        this(0,0);
    }
    
    String getXY() {
        return "("+x+","+y+")";
    }
}
// 원은 도형이다 -> 상속, 원은 점을 가지고 있다 -> 포함
class Circle extends Shape{    //상속 관계
    Point center;    // 포함 관계
    int r;    //반지름
    
    Circle(){
        this(new Point(0,0),100); // 0,0 위치의 반지름 100인 원(Circle)
    }
    
    Circle(Point center, int r){
        this.center = center;
        this.r =r;
    }
    
    // 조상 클래스인 Shape클래스에도 draw()가 선언되어 있으나
    // Circle클래스의 draw()가 호출된다. (오버라이딩)
    void draw() {
        System.out.printf("Circle draw : [center=(%d, %d), r=%d, color=%s]%n", center.x, center.y, r, color);    //color 상속 받음
    }
}
 
class Triangle extends Shape{    // 상속 관계
    Point[] p = new Point[3];    // 포함 관계
    
    Triangle(Point[] p) {    // 매개 변수를 참조 배열로 받는다.
        this.p = p;
    }
    
    void draw() {
        System.out.printf("Triangle draw : [p1=%s, p2=%s, p3=%s, color=%s]%n",p[0].getXY(), p[1].getXY(), p[2].getXY(), color);
    }
}
 
public class Test {    
    public static void main(String[] args) {
        Point[] p = {    //참조 배열로 선언 후
                new Point(100100),
                new Point(14050),
                new Point(200100)
        };
        
        Triangle t = new Triangle(p);    // 참조 배열을 생성자로 전달
        Circle c = new Circle(new Point(150150), 50);
        //Point p = new Point();
        //Circle c = new Circle(p,50)의 코드를 합친 것이 위의 코드
        
        t.draw();    // 삼각형 그리기
        c.draw();    // 원 그리기
    }
}

-       삼각형(Triangle), (Circle)을 그리는 클래스로 도형을 의미하는 Shape클래스는 상속, 도형의 위치(좌표)를 의미하는 Point클래스는 포함관계로 작성한다.

-       삼각형(Triangle), (Circle) 클래스에서 Point클래스를 변수로 이용해 도형의 좌표를 지정한다.

-       삼각형(Triangle), (Circle) 클래스에서 draw() 메소드는 조상 클래스인 Shape에도 있지만 호출 시 각각의 클래스에서 선언한 draw()메소드가 실행되는데 이를 오버 라이딩이라고 한다.

 

단일 상속

o  다른 객체지향언어인 C++에서는 하나의 클래스에서 여러 조상 클래스를 상속받는 것이 가능한 다중 상속(multiple inheritance)하나 자바에서는 단일 상속만을 허용한다.

o  다중 상속을 하게 되면 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있는 장점이 있지만 클래스 간의 관계가 매우 복잡해지고 서로 다른 클래스로부터 상속받은 멤버 간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점이 있다. 자바에서는 이러한 단점으로 다중 상속을 포기하고 단일 상속만을 허용함으로써 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다는 점에서 다중 상속보다 유리하다.

 

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

o  Object클래스는 모든 클래스 상속계층도의 최상위 위치에 있는 조상클래스이다. 다른 클래스로부터 상속받지 않는 모든 클래스는 자동적으로 Object클래스로부터 상속받게 되어있기에 가능하다.

class Tv{
 
}
class Tv extends Object{
    
}

-       클래스 작성 시 상속받는 것이 없으면 컴파일 시 자동으로 Object클래스를 상속받도록 추가한다.

o  Object클래스를 자동 상속받게 함으로써 toString()이나 equals()와 같은 메소드를 정의하지 않고 사용할 수 있던 것이다.