Java

상속

상속이란?

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

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

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

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

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

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

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

부모 클래스와 자식 클래스의 관계를 상위(super) 클래스와 하위(sub) 클래스라고 표현하기도 한다. 또한 기초 클래스(base class), 유도 클래스(derived class)라고도 부른다. 
package org.opentutorials.javatutorials.Inheritance.example1;

class Calculator {
    int left, right;

	public void setOprands(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 SubstractionableCalculator extends Calculator {
	public void substract() {
		System.out.println(this.left - this.right);
	}
}

public class CalculatorDemo1 {

	public static void main(String[] args) {

		SubstractionableCalculator c1 = new SubstractionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}

}

실행결과는 아래와 같다.

30
15
-10

위의 코드를 하나씩 분해해서 생각해보자.

class SubstractionableCalculator extends Calculator {
    public void substract() {
		System.out.println(this.left - this.right);
	}
}

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

SubstractionableCalculator c1 = new SubstractionableCalculator();
c1.setOprands(10, 20);
c1.sum();
c1.avg();
c1.substract();

이것이 가능한 이유는 extends Calculator 때문이다. 이것은 클래스 Calculator를 상속 받는다는 의미다. 따라서 SubstaractableCalculator는 Calculator에서 정의한 메소드 setOprands, sub, avg를 사용할 수 있게 된다. 이것이 프로그래밍의 역사에서 대단한 진전으로 평가받는 상속의 기본적인 의미다. 상속을 통해서 코드의 중복을 제거할 수 있었고, 또 부모 클래스을 개선하면 이를 상속받고 있는 모든 자식 클래스들에게 그 혜택이 자동으로 돌아간다. 다시 말해서 유지보수가 편리해진다는 것이다.  재활용성과 중복의 제거, 그리고 유지보수의 편의는 서로 다른 목적으로 가지고 있지만, 하나가 좋아지면 자연스럽게 다른 쪽도 좋아지는 관계에 있다는 것을 다시 한 번 환기해주는 대목이다.

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

package org.opentutorials.javatutorials.Inheritance.example1;

class MultiplicationableCalculator extends Calculator {
    public void multiplication() {
		System.out.println(this.left * this.right);
	}
}

public class CalculatorDemo2 {

	public static void main(String[] args) {

		MultiplicationableCalculator c1 = new MultiplicationableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.multiplication();
	}

}

결과는 아래와 같다.

30
15
200

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

package org.opentutorials.javatutorials.Inheritance.example1;

class DivisionableCalculator extends MultiplicationableCalculator {
    public void division() {
		System.out.println(this.left / this.right);
	}
}

public class CalculatorDemo3 {

	public static void main(String[] args) {

		DivisionableCalculator c1 = new DivisionableCalculator();
		c1.setOprands(10, 20);
		c1.sum();
		c1.avg();
		c1.multiplication();
		c1.division();
	}

}

이것이 상속의 기본적인 개념이다. 어렵지 않다. 하지만 장점이 있으면 단점도 있는 법이다. 프로그래밍의 세계에서는 이 상속의 효용을 수용하기 위해서 꽤나 많은 대가를 치러야 했다. 그 대가를 한마디로 표현하자면 복잡도의 증가라고 이야기할 수 있을 것이다. 객체지향을 두 개의 시즌으로 나눈다면 클래스와 인스턴스에 대한 이해가 시즌1이라면 상속은 객체지향 내에서의 시즌2라고 할만하다. 

댓글

댓글 본문
  1. 24.01.19 완료
  2. 서달
    20230323
  3. coster97
    .
  4. wwwqiqi
    완료
  5. infernist
    2022/10/31
  6. MelonMusk
    09/04
  7. 람보
    2022.9.2
  8. 감사합니다~
  9. 너굴
    220810
  10. 치키티타
    220809
  11. 김경모
    220622
  12. PassionOfStudy
    복습 2일차!
  13. 치키티타
    220620
  14. PassionOfStudy
    상속!
  15. 김은희
    20220601 완료 ..^^ 화이팅하자 ㅅ
  16. 자바잡아
    22.05.02 상속 복습 4회차 완료
  17. 20220426
  18. dogchang
    상속이 눈에 안익어서 안보고 할때까지 하느라 삼일째 반복중입니다.

    public class xxxxxxx{
    public static void main(String[] args) {
    Test c1 = new Test

    c1.setOprands(10,20);
    c1.firsttest();
    }
    }

    class Test extends Calculator {
    public void firsttest() {
    System.out.println("입력된 수는"+this.left+"와"+this.right+"입니다.");
    }
    }

    다른 클래스 파일에서 main에 class Test를 또 상속받아서 사용할수있다. 이 이야기 이신것을 이해하는데 좀 걸렸습니다. 상속에 상속을 하면 가장 첫번째 클래스의 메소드를 동일하게 사용가능하기때문에 본문의 Oprands(); 를 사용할수있다는걸 알고 가는 시간이었습니다.
  19. aesop0207
    220323 Wed.
  20. 모찌말랑카우
    22.02.14 완료
  21. aesop0207
    220207 Mon
  22. 행달
    22.02.05 완료!!!
  23. syh712
    2021-12-06
    <상속>
    1. 부모 클래스와 자식 클래스의 관계를 상위(super) 클래스와 하위(sub) 클래스라고 표현하기도 한다. 또한 기초 클래스(base class), 유도 클래스(derived class)라고도 부른다.
    2. Class SubtractionableCalculator(상속) extends Calculator(피상속)
  24. 드림보이
    2021.12.03. 상속 파트 수강완료
  25. IaaS
    2021-11-01 수강완료

    실제로 안드로이드 스튜디오로 앱 개발을 하다보면 override 및 extends를 사용하는 경우가 많고 활용성이 광범위하다.
  26. 성치
    2021-11-01 완료
  27. H4PPY
    1017
  28. 미NI언
    10.11 완료!
  29. 멋을아는남자
    학습하고갑니다.감사합니다. 조금씩 이해가 되고 있네요
  30. 베이스박
    210825 학습했습니다. 감사합니다.
  31. super1Nova
    210820
  32. 이땅콩
    상속의 개념은 알겠는데,
    자식 클래스에서 부모 클래스를 상속하면, 부모클래스의 생성자는 못 쓰나봐요?
    계속 오류가 나네요!
    다음 강의에 제대로 배워보겠습니다!
  33. 개발꾸꾸
    5.17
  34. 악어수장
    2021-5-13
  35. 드림보이
    수강완료했습니다...
  36. 하연주
    210207 완료
  37. 윤지
    상속 받아서 그래요 calculatordemo2 는 단순히 파일 이름이라고 생각하시면 되구요
    c1 이라는 객체를 생성하는 객체 타입을 따라가 보면 맨 위에 클래스 선언한게 보일겁니다.

    =====
    extends 라는 것을 사용하여 부모 객체인 calculator를 상속 받았기 때문에
    따로 생성하지 않아도 사용할 수 있는거에요. 영상 조금 더 보시면 이해 갈 것 같네요
    대화보기
    • CaseStyle
      이번시간의 주제는 상속입니다. 상속이란 객체의 이점을 더욱 좋게 하기위해서 상속을 함으로써 불필요한 코드의 재사용을 줄입니다. 그래서 calculatordemo1에서 calculator 클래스를 정의하였고, calculatordemo2에서는 클래스를 class MultiplicationableCalculator extends Calculator 이렇게 선언함으로써 Calculator의 기능을 MultiplicationableCalculato 여기에서 상속받을수 있도록 지정해주었기 때문에 곱하기 메소드만 만들어서 사용을 할수있는것입니다.
      대화보기
      • 김태현
        2치 수업완료
      • 으헝
        calculatordamo1에서 calculator 클래스를 정의했다면
        calculatordamo2에서 calculator클래스를 정의하지 않고 바로 사용 가능한 이유가 뭔가요? ㅠㅠ
      • hvii
        20200811 학습완료
      • yulrinam
        200810 시청완료 감사합니다 :)
      • 김요한
        하나의 클래스에서 두개의 클래스를 선언할 수 있나요?
        아무리 해도 안 되는데요.....
      • 김승민
        2020-04-10 시청완료
        감사합니다 (__~
      • 흐무
        파일명과 클래스명이 동일하지 않아서 그래요.
        대화보기
        • 기초부터탄탄
          도대체 왜 CalculatorDemo1 에서 The public type CalculatorDemo1 must be defined in its own file 라는
          에러가 뜨는건가요.. 예제랑 똑같은 소스인데.................진짜 이유를 모르겠습니다

          class test_1 {
          int a, b;

          public void setVariable(int a, int b) {
          this.a = a;
          this.b = b;
          }

          public void sum() {
          System.out.println(this.a + this.b);

          }

          public void multiply() {
          System.out.println(this.a * this.b);

          }
          }

          class SubstractionableCalculator extends test_1 { // Calculaotr 부모클래스 상속
          public void minus() {
          System.out.println(this.a - this.b);
          }
          }

          public class CalculatorDemo1 {
          public static void main(String[] args) {
          SubstractionableCalculator sac = new SubstractionableCalculator();
          sac.setVariable(40, 20);
          sac.sum();
          sac.multiply();
          sac.minus();
          }
          }
        • aminora
          간단하게 보자면 class 새로운클래스 extends 기존 클래스{추가할 기능들~}이렇게 보면 되는거네요.
          세이브 로드 개념으로 보니 이해가 잘되는것 같습니다.
        • 우엥
          모르...겠어....
        • 프밍프밍
          감사합니다!!!
        • 허공
          감사합니다!