[Java] 22. 상속

psj98·2023년 1월 8일
0

생활코딩 JAVA

목록 보기
22/41
post-custom-banner

1. 상속이란?

객체지향을 통해서 달성하고자 하는 목표 중에서 가장 중요한 것은 재활용성일 것이다. 상속은 객체지향의 재활용성을 극대화시킨 프로그래밍 기법이라고 할 수 있다. 동시에 객체지향을 복잡하게 하는 주요 원인이라고도 할 수 있다.

상속(Inheritance)이란 물려준다는 의미다. 어떤 객체가 있을 때 그 객체의 필드(변수)와 메소드를 다른 객체가 물려 받을 수 있는 기능을 상속이라고 한다. 부모와 자식의 관계에 따른 비유를 들을 수도 있지만, 비유는 얻는 것보다 잃는 것이 많기 때문에 구체적인 코드를 통해서 상속을 알아보자.

CalculatorDemo 예제에서 등장하는 객체 Calculator는 더하기와 평균에 해당하는 sum과 avg 메소드를 가지고 있다. 그런데 이 객체가 가지고 있는 기능에 빼기를 추가하고 싶다. 가장 쉬운 방법은 이 객체에 빼기를 의미하는 substract를 추가해서 아래와 같이 사용하고 싶다.

Calculator c1 = new Calculator();
c1.setNum(10, 20);
c1.sum();
c1.avg(); 
c1.substract();

아래와 같은 경우에 속한다면 객체에 메소드를 추가하는 것이 어렵다.

  • 객체를 자신이 만들지 않았다. 그래서 소스를 변경할 수 없다. 변경할 수 있다고 해도 기존 소스를 업데이트하면 메소드 substract이 사라진다. 이러한 문제가 일어나지 않게 하기 위해서는 지속적으로 코드를 관리해야 한다.
  • 객체가 다양한 곳에서 활용되고 있는데 메소드를 추가하면 다른 곳에서는 불필요한 기능이 포함될 수 있다. 이것은 자연스럽게 객체를 사용하는 입장에서 몰라도 되는 것까지 알아야 하는 문제가 된다.

2. 상속 이해하기

이제부터 언어의 개발자가 되어 보자. 기존의 객체를 그대로 유지하면서 어떤 기능을 추가하는 방법이 없을까? 이런 맥락에서 등장하는 것이 상속이다. 즉 기존의 객체를 수정하지 않으면서 새로운 객체가 기존의 객체를 기반으로 만들어지게 되는 것이다. 이때 기존의 객체는 기능을 물려준다는 의미에서 부모 객체가 되고 새로운 객체는 기존 객체의 기능을 물려받는다는 의미에서 자식 객체가 된다. 그 관계를 반영해서 실제 코드로 클래스를 정의해보자.

class Calculator {
    int left, right;
 
    public void setNum(int left, int right) {
        this.left = left;
        this.right = right;
    }
 
    public void sum() {
        System.out.println(this.left + this.right);
    }
 
    public void avg() {
        System.out.println((this.left + this.right) / 2);
    }
}
 
class SubCalculator extends Calculator {
    public void substract() {
        System.out.println(this.left - this.right);
    }
}
 
public class CalculatorDemo {
    public static void main(String[] args) {
        SubCalculator c1 = new SubCalculator();
        c1.setNum(10, 20);
        c1.sum();
        c1.avg();
        c1.substract();
    }
}

/* 실행 결과 */
> 30
> 15
> -10

우선 새로운 클래스인 SubCalculator를 정의했다. 이 클래스의 본체에는 sbstract라는 메소드만 존재한다. 하지만 이 클래스를 인스턴스화한 c1은 정의하지 않은 메소드들을 호출하고 잘 동작한다.

이것이 가능한 이유는 extends Calculator 때문이다. 이것은 클래스 Calculator를 상속 받는다는 의미다. 따라서 SubCalculator는 Calculator에서 정의한 메소드 setNum, add, avg를 사용할 수 있게 된다. 이것이 프로그래밍의 역사에서 대단한 진전으로 평가받는 상속의 기본적인 의미다.

상속을 통해서 코드의 중복을 제거할 수 있었고, 또 부모 클래스를 개선하면 이를 상속받고 있는 모든 자식 클래스들에게 그 혜택이 자동으로 돌아간다. 다시 말해서 유지보수가 편리해진다는 것이다. 재활용성과 중복의 제거, 그리고 유지보수의 편의는 서로 다른 목적으로 가지고 있지만, 하나가 좋아지면 자연스럽게 다른 쪽도 좋아지는 관계에 있다는 것을 다시 한 번 환기해주는 대목이다.

생각을 조금 더 말랑하게 하기 위해서 Calculator를 상속받는 클래스를 하나 더 만들어보자. 이 클래스는 곱하기를 할 수 있는 클래스다.

class MultiCalculator extends Calculator {
    public void multiplication() {
        System.out.println(this.left * this.right);
    }
}
 
public class CalculatorDemo {
    public static void main(String[] args) {
        MultiCalculator c1 = new MultiCalculator();
        c1.setNum(10, 20);
        c1.sum();
        c1.avg();
        c1.multiplication();
    }
}

/* 실행 결과 */
> 30
> 15
> 200

그럼 상속한 클래스를 다시 상속할 수 있을까? 물론 가능하다. 아래의 예제는 곱하기가 가능한 클래스인 MultiCalculator를 상속받고 있다.

class DivCalculator extends MultiCalculator {
    public void division() {
        System.out.println(this.left / this.right);
    }
}
 
public class CalculatorDemo {
    public static void main(String[] args) {
        DivCalculator c1 = new DivCalculator();
        c1.setNum(10, 20);
        c1.sum();
        c1.avg();
        c1.multiplication();
        c1.division();
    }
}

이것이 상속의 기본적인 개념이다. 하지만 장점이 있으면 단점도 있는 법이다. 프로그래밍의 세계에서는 이 상속의 효용을 수용하기 위해서 꽤나 많은 대가를 치러야 했다. 그 대가를 한마디로 표현하자면 복잡도의 증가라고 얘기할 수 있을 것이다.


3. 참고

생활코딩

profile
SSAFY 9기
post-custom-banner

0개의 댓글