본문 바로가기
✏️Java 공부/Java의 정석

[Java 공부/Java의 정석] Chapter.07 : 객체지향 프로그래밍 2 - 1 (상속)

by 코코의 주인 2022. 7. 4.

상속

 상속은 기존 클래스를 재사용하여 새로운 클래스를 작성하는 것을 말한다. 상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다.

 상속을 구현하는 법은 간단하다. 새로 작성하고자 하는 클래스의 이름 뒤에 상속받고자 하는 클래스의 이름을 키워드 'extends'와 함께 써주면 된다.

class Child extends Parent {
	...
}

 이 때 두 클래스는 서로 상속 관계에 있다고 하며, 상속해주는 클래스를 '부모 클래스', 상속받는 클래스를 '자식 클래스'라고 한다. 자식 클래스는 부모 클래스의 모든 멤버(변수, 메서드)를 상속받기 때문에 항상 조상 클래스보다 같거나 많은 멤버를 갖는다.

예시)

class parent {
    int age;
}

class child extends parent {
    void cry() {
        System.out.println("응애");
    }
}

public class inheritance1 {
    public static void main(String[] args) {
        parent p = new parent();
        child c = new child();
        p.age = 35;
        c.age = 1;

        System.out.printf("부모의 나이는 %d살 입니다.\n", p.age);   //부모의 나이는 35입니다.
        System.out.printf("자식의 나이는 %d살 입니다.\n",c.age);    //자식의 나이는 1입니다.

        c.cry();	//응애
    }
}

 위 예제는 자식 클래스(child)를 부모 클래스(parent)로부터 상속 받은 예시이다. 자식 클래스에 age라는 변수를 선언하지 않았음에도 부모로부터 멤버를 상속받았기 때문에 자식 클래스인 child에서도 age라는 변수를 사용할 수 있는 것을 볼 수 있다. 또 부모 클래스에는 없는 새로운 메서드인 cry()를 추가하여 사용할 수 있다.


포함 관계

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

 

예제

class circle {
    int x;	//x좌표
    int y;	//y좌표
    int r;	//반지름
}

 원을 표현하기 위한 circle 클래스를 위와 같이 작성하였다. 이제 이 클래스를 포함관계를 사용한 클래스로 바꿔보자.

class point {
    int x;
    int y;
}

 원을 나타내기 위한 원점도 하나의 좌표이기 때문에, 좌표상의 한 점을 다루기 위한 point 클래스를 작성한다. 

class circle {
    point p = new point();
    int r;
}

 그리고 x, y 좌표를 나타내던 변수를 point클래스를 사용해서 나타낸다.

 이런 방식으로 한 클래스의 멤버변수로 다른 클래스 타입의 참조 변수를 선언하는 것을 포함관계를 맺는 것이라 한다.

 

예제

class pointer {	//좌표
    int x;
    int y;

    pointer() {
        this(0,0);
    }
    pointer(int x, int y) {
        this.x = x;
        this.y = y;
    }
}
class circle {
    pointer center;	//원점
    int radius;	//반지름
    circle() {
        this(new pointer(0,0), 10);
    }
    circle(pointer p, int r) {
        this.center = p;
        this.radius = r;
    }
}
public class composite {
    public static void main(String args[]) {
        circle c1 = new circle();
        circle c2 = new circle(new pointer(1,2), 100);
        pointer p = new pointer(3,4);
        circle c3 = new circle(p,30);

        System.out.printf("c1의 원점 좌표는 (%d,%d)이고 반지름은 %d입니다.\n", c1.center.x, c1.center.y, c1.radius);
        System.out.printf("c2의 원점 좌표는 (%d,%d)이고 반지름은 %d입니다.\n", c2.center.x, c2.center.y, c2.radius);
        System.out.printf("c3의 원점 좌표는 (%d,%d)이고 반지름은 %d입니다.\n", c3.center.x, c3.center.y, c3.radius);
    }
}
 실행 결과

c1의 원점 좌표는 (0,0)이고 반지름은 10입니다.
c2의 원점 좌표는 (1,2)이고 반지름은 100입니다.
c3의 원점 좌표는 (3,4)이고 반지름은 30입니다.

 포함 관계를 사용하여 원을 나타낸 예시다. 저번 글에서 배운 생성자를 사용하여 초기화하였다.


클래스 간의 관계 결정하기

 클래스를 작성하는 데 있어서 상속관계를 맺어 줄 것인지 포함관계를 맺어줄 것인지 결정하는 것이 혼란스러울 수도 있다. 그럴 때는 'is-a(~은 ~이다)' 관계와 'has-a(~은 ~을 가지고 있다)' 관계로 문장을 만들어보자. is-a 문장이 말이 되면 상속 관계로, has-a 관계가 말이 되면 포험 관계로 관계를 맺어주면 된다.

is-a 관계 : 원은 점이다. - (X)
has-a 관계 : 원은 점을 가지고 있다. - (O)
-> 포함 관계

is-a 관계 : 개는 동물이다. - (O)
has-a 관계 : 개는 동물을 가지고 있다. - (X)
-> 상속 관계

단일 상속

 다른 객체지향 언어인 C++에서는 여러 조상 클래스로부터 상속받는 것이 가능한 '다중 상속'을 허용하지만 Java에서는 한 조상에게만 상속을 받을 수 있는 단일 상속만을 허용한다. 다중 상속을 허용하면 여러 클래스로부터 상속을 받을 수 있기 때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다는 장점이 있지만, 클래스 간의 관계가 매우 복잡해진다는 것과 서로 다른 클래스로부터 상속받은 멤버 간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점을 가지고 있다.

 단일 상속은 하나의 조상 클래스만을 가질 수 있기 때문에 다중 상속에 비해 불편한 점도 있지만, 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다는 점에서 다중 상속보다 유리하다.


총평

나는 객체 지향하면 제일 먼저 생각나는 것이 상속이다.

다음 글에서는 오버라이딩에 대해서 알아볼 것인데 그러면 상속에 대한 내용은 대부분 이해할 수 있을 것이라 생각한다.

 

댓글