본문 바로가기

프로그래밍/Java

19.12.26) Java - Inheritance(상속)

목차

  • 수평적 설계와 수직적 설계
  • 상속 체이닝
  • 상속과 Override의 활용 : TV와 리모콘
  • 부모 클래스에 상태 정보가 있는 경우

핵심

  • 상속은 자식이 부모의 기능을 사용할 수 있다는 의미이다.(물려 받는다는 의미가 아니다. 확장의 의미이다.)

Class를 행위 정보 위주로 바라보자.

그 동안 클래스를 바라보는 관점은 다양한 데이터를 담는 자료형으로 살펴봤다. 바구니 역할 즉, 상태 정보에 초점을 맞춘 관점이었다. 하지만 이제는 객체의 행위 정보에 초점을 맞춰서 이야기를 해보자. 먼저 상속에 대해서 알아보자.

수평적 설계와 수직적 설계

클래스는 객체를 설계하는 도구라는 의미는 변함없다. 그 중 설계하는 방법으로 크게 2가지가 있다. 수평적 설계와 수직적 설계인다. 수평적 설계는 각 클래스를 독립적 설계하는 방법을 말한다. 독립적이라는 말은 좋아보이지만 코드를 관리하는 입장에선 그닥 좋진 않다. 만약 100개의 클래스가 비슷한 상태 정보과 행위 정보를 가지고 있다면 100개 클래스에 같은 코드를 입력해야한다. 너무나 비생산적이다. 개발자는 이런 광경을 눈 뜨고 못 본다.

수평적으로 클래스를 설계하면

  1. 코드의 중복이 발생하고
  2. 코드 관리가 어려워진다.

반면에, 수직적 구조는 독립적으로 클래스를 설계하지 않고 계층을 두고 설계하는 방법이다. 마치 부모, 자식처럼 말이다. 만약 개, 고양이 클래스를 만들어야 할 때 이 둘을 포괄하는 동물이라는 클래스를 만들어서 개, 고양이 클래스가 그 정보를 상속 받으면 되지 않을까?

수평적 설계는

  1. 코드의 중복을 줄일 수 있고
  2. 코드 관리가 수평적 설계보다 쉬워진다.

이 때 자식 클래스인 개, 고양이는 기존 기능에서 확장을 했기 때문에 extends라는 키워드로 상속 받는다. 그리고 부모 클래스를 super class라고 부르고 자식 클래스를 sub class라 부른다.

public class Dog extends Animal {
    public static void main(String[] args) {
    }
}

상속 체이닝(Inheritance Chaining)

상속 체이닝은 가장 상위 클래스부터 객체가 생성된 뒤 연쇄적(Chaining)으로 하위 클래스로 객체가 생성되는 방법을 말한다. 왜 이 방법이 쓰이냐면 실생활 예시를 들자면, 내가 존재하기 위해선 반드시 부모님이 먼저 태어나야 한다. 만약 개라는 객체를 만들기 위해선 상위 클래스는 동물이라는 클래스가 만들어져야 한다. 이 과정은 super()라는 키워드로 이루어진다.

public class Animal{ // super 클래스
    private String name;
    private int weight;
}

public class Dog extends Animal {
    public void eat() {
        System.out.println("와구와구");
    }
    public Dog() {
        super() // Dog 객체를 생성할 때 생성자 메서드 안에 부모 객체 생성자 super()가 있어서 상위 클래스인 Animal이 메모리에 먼저 생성된다. 그 다음 Dog가 생성된다.
    }
}

상속과 Override의 활용 : TV와 리모콘

상속을 할 경우 여러가지 장점이 있지만 다른 관점에서 살펴보자. 지금까지는 내가 설계한 클래스를 사용했기 때문에 어떤 메서드가 있는지 알고 쓸 수 있다. 하지만 다른 사람이 내 객체를 사용할 때는 어떤 기능이 있는지 전부 알지 못한다. 왜? 그들에게 배포할 때 java파일(소스코드)를 배포하면 보안의 위험이 있으니 byte code로 컴파일된 파일을 배포한다. 그럼 상대방은 소스코드가 없으니 내 객체를 제대로 쓸 수가 없다. 그러면 이 때 필요한 건 내 객체를 사용할 수 있는 도구가 필요하다. 리모콘

우리가 TV를 살 때 리모콘을 같이 사는 이유는 TV를 사용하기 위해서다. 리모콘이 없이 TV를 사용하려면 TV 속 모든 기능을 다 알아야한다. 하지만 회사에서 TV 기능이 담긴 설계도를 줄 수는 없는 노릇이다. 대신에 회사에는 리모콘을 제공한다. 사용자는 TV가 속에서 어떻게 작동하는지 알 수는 없지만 리모콘이 있다면 TV를 사용할 수 있다.

다시 본문으로 돌아와서 소스 코드 없이도 내 객체를 다른 사람이 쓸 수 있게 하려면 리모콘 역할이 필요하다. 이 때 Override 기능이 활용된다. 하위 메서드가 Override(재정의) 되면 상위 객체로 하위 메서드에 접근할 수 있다. 만약 사용자가 하위 메서드의 소스 코드를 보지 못해도 상위 객체를 통해서 리모콘처럼 사용 가능하다. 단, 메서드가 override(재정의)가 되어 있어야한다.

자동 형변환(upCasting), 강제 형변환(downCasting)

자식 객체를 부모 객체 변수에 담는 것을 자동 형변환(upCasting)이라 한다. 부모와 자식은 상속 관계이기 때문에 자동으로 형변환이 된다.

Animal ani = new Dog();

부모 객체 변수를 자식 객체로 바꾸는 것을 강제 형변환이라고 한다. 부모를 자식으로 바꾸는 일이니 자동으로 이루어지지 않고, 강제적으로 이루어지는 것이다.

((Cat)ani).night();

부모 클래스에 상태 정보가 있는 경우

부모 클래스에서 상태 정보가 선언되면 부모의 기능을 이어받는 자식 클래스도 생성자에서 부모의 생성자를 초기화 해줘야 한다. 그래야 객체를 생성할 때 부모 객체도 올바르게 생성된다.

public class Dog extends Animal {
    public Dog(String name, int weight) {
        super(name, weight);
}