생활코딩

Coding Everybody

코스 전체목록

닫기

상속

상속이란?

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

상속(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. doevery
    수강완료
  2. 지미츄
    감사합니다
  3. 자스
    ㅎㅎㅎ
    대화보기
    • 유정이
      상속 2/3에서 4분 25초에 트림하셨죠
    • 얼그레이
      파이썬에서도 배웠지만 상속이란 개념이 참 재밌는 거 같아요 :) 감사합니다! 190710
    • 6/17시작
      6월 25일 완료
    • 이현정
      6.24 8시 15분 완료
    • JLetter
      감사합니다.
    • 에듀케이티드키드
      감사합니다!~
    • 이호준
      말씀하신 것을 다중상속이라고 하는데, Java는 다중상속을 허락하지 않아요~
      대화보기
      • silver94
        감사합니다
      • Gukita
        자바에선 하나만 가능합니다.
        대화보기
        • 라또마니
          감사합니다. 반복해서 듣고 또 듣고 나면 내용이 이해가 가요~
        • 닷미
          두개의 서로다른 클래스를 하나의 클래스로 상속할 수도 있나요?
          그러면 extends 뒤에 두개의 클래스를 써야하는건지... 불가능한지..
        • 호두
          고맙습니다.
          extends
        • Dark Theme very Goood^^
        • j-graphy
          2019. 2. 7. 학습완료
        • andrewjkme
          글의 내용을 보니 아직 헷갈려하시는거 같아서 댓글 남겨요.
          public class CalculatorDemo {

          }
          이 클래스는 다른 두 클래스를 작동시키기 위한 클래스 입니다.
          집에 계산기가 있는데 숫자를 두드려서 입력하지 하지 않으면 결과를 얻을 수 없는것처럼
          위 클래스는 숫자를 입력하는 클래스 라고 생각하시면 됩니다.

          최상위 클래스라는 표현은 여기에서 적절하지 않습니다.
          클래스 안에 클래스는 이너클래스라고 한 클래스 안에 또다른 객체를 구현할때(예를 들어 ListNode) 필요하지만,

          여기서 Calculator와 SubtractionableCalculator에서는 해당되지 않습니다. 뺄셈 계산기는 그냥 계산기를 상속 받았을 뿐이지, 종속되어있는 이너클래스가 아닙니다.

          아직도 헷갈리시면 이클립스에서 한 패키지 안에 Calculator.java, SubtractionableCalculator.java, CalculatorDemo.java 3개를 생성하고 각각 따로 저장해서 하나씩 실행시켜보세요.

          Calculator, SubtractableCalculator는 아예 실행조차 안되고 (main 메소드가 없기때문에), CalculatorDemo가 실행이 되어야 결과를 얻을 수 있을꺼예요
          대화보기
          • 현림
            public class CalculatorDemo {

            }

            이 클래스가 왜 필요한건지 제가 이해를 못했어요;;;ㅠㅠ

            최상위클래스:Calculator
            상속받으려는클래스:SubstractionableCalculator

            저는 이렇게 2개만 있으면 되는거라고 생각했는데 제가 크나큰 착각을 하고있나바요;;;
          • andrew
            해결책을 찾으셨나요?
            확실한건 아니지만 각 클래스 마다 끝나는 지점이 잘 못 되어 있는거 같습니다.

            class Calculator {
            ...
            }

            class SubstratableCalculator extends Calculator {
            ...
            }

            public class CalculatorDemo {
            public static void main(String[] args) {
            ....
            }
            }

            이처럼 뺄샘계산기 클래스는 부모 클래스 밖에 있어야 합니다.
            또한 파일이름과 같은 클래스도 두 계산기 클래스 밖에 있어야 합니다.
            대화보기
            • 현림
              아뇨 대입해도 에러가 똑같이 납니다.

              public class CalculatorDemo(){}

              이것을 예제와 같이 입력해주면 에러가 나는대요.

              CalculatorDemo 클래스를 만들어서 정의를 해야 에러가 사라지는것인지 모르겟네요;;
            • 병옥
              저도 잘 모르지만 소스코드에서 입력이 잘 못된것 같습니다.
              아래와 같이 수정하면 잘 돌아갑니다.

              package org.opentutorials.javatutorials.io;

              class Calculator {
              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 Calculator{ //Calculaotr 부모클래스 상속
              public void minus(){
              System.out.println(this.a - this.b);
              }
              }

              public class CalculatorDemo{
              public static void main(String[] args) {
              SubstractionableCalculator sac=new SubstractionableCalculator();
              sac.setVariable(40, 20);
              sac.sum();
              sac.multiply();
              sac.minus();
              }
              }


              60
              800
              20
              대화보기
              • 현림
                동일하게 하는데 자꾸 에러가 납니다.
                이유가 뭔지 모르겟어요;;ㅠ

                public class Calculator {
                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 Calculator{ //Calculaotr 부모클래스 상속
                public void minus(){
                System.out.println(this.a - this.b);
                }
                }
                public static void main(String[] args) {
                SubstractionableCalculator sac=new SubstractionableCalculator();
                sac.setVariable(40, 20);
                sac.sum();
                sac.multiply();
                sac.minus();
              • 기수니
                1년동안 JAVA공부한거 다 까먹어서 다시 보고 있어염
              • 렐론
                자막 올려놨어요! 1번만..
              • ㅇㅇ
              • ㅇㅇ
              • 전민희
                2018/10/06 완료!
              • 0.9459465568420731
              • 전하연
                감사합니다
                2018.8.19 14:44
              • 이태호
                7/4
              • DAIHO
                그런데 자바는 잘 모르겠지만 매트랩 같은 경우에는 상속을 여러개에서 받을수 있더라고요.
                대화보기
                • javamaster let's go~
                  제가 알기로
                  하나의 클래스에는 하나의 클래스만 상속이 가능합니다~
                  부모는 무조건 1명 자식은 여러명일 수 있습니다~
                  대화보기
                  • DAIHO
                    다중으로 superclass에 상속받을 수 있나요?
                  • ubiquitous4g
                    봐도봐도.. 화살표는 상속의 이해를 더 어렵게 함.

                    상속에서 화살표 방향이 위로 향하는 이유

                    출처 : Java언어로 배우는 디자인 패턴 입문

                    <화살표의 방향>
                    UML에서는 하위 클래스에서 상위 클래스를 향해 화살표가 뻗어 있습니다.
                    상위 클래스를 기준으로 하위 클래스를 만들기 때문에 화살표를 반대로 표시하는 것이 이해하기 쉽다고 생각할지도 모릅니다.

                    다음과 같이 생각하면 이해하기 쉽습니다.

                    하위 클래스를 정의할 때 extends(확장)로 상위 클래스를 지정합니다.
                    따라서 하위 클래스는 반드시 상위 클래스를 알고 있습니다.
                    그러나 상위 클래스는 하위 클래스를 알고 있다고 할 수 없습니다.
                    상대를 지목할 수 있는 것은 상대를 알고 있을 때 뿐입니다.
                    그래서 하위 클래스에서 상위 클래스로 화살표를 표시하는 것입니다.
                  • 공동공구
                    저도 확실히 모르겠어서 궁금한 부분이긴 하네요. 수직관계(부모-자식)이 아닌 수평관계(자식-자식)에서 상속의 가능여부를 물어보신 것 같은데, 제 생각엔 이 땐 상속이 아니라 자식1이 자식2를 import 해서 그 안의 객체들을 사용할 수 있을 것 같네요. import와 상속은 조금 다를 겁니다.
                    대화보기
                    • 열정하나
                      클레스의 메소드와 변수들을 상속 받는다.
                    • 5월 2일 상속 예전에 배웠을때 상속이 솔직히 이해가 잘 가지 않았는데 이번 기회에 이해가 쏚쏚
                    • 박현모
                      감사합니다~ 잘 봤습니다~
                    • egoya
                      궁금합니다.. 자식들끼리는 상속이 불가능한가요?
                    • JeongHo Park
                      잘들었습니다!!!
                    • 김세창
                      검정화면 완전 맘에 드네요~~~
                      수업내용은 매우 만족!!
                      이젠 어떤 라이브러리도 쉽게 사용할수잇을것같네염 ㅋㅋ
                    • 하면된다하자
                      완료
                    • popinbompin
                      완료
                    • henry
                      class DivisionCalculator extends MultiCalculator{

                      public DivisionCalculator(int left, int right) {
                      this.left = left;
                      this.right = right;
                      }

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

                      public static void main(String[] args) {
                      DivisionCalculator c1 = new DivisionCalculator(40, 20);

                      Division에 매개변수 정의해주고 생성자로 전달하니 오류없이 잘 되네요^^.
                    • selina
                      강의 더 보니까 <상속과 생성자> 2/2 영상 5:00~ 보시면 부모클래스의 기본 생성자를 만들어주지 않아서 그런거 같네요 ㅎㅎ
                      대화보기
                      • cantsto
                        class Calculator {
                        int left, 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 SubstractionableCalculator (int left, int right) {
                        this.left = left;
                        this.right = right;
                        System.out.println(left - right);
                        }
                        }

                        public class CalculatorDemo1 {

                        public static void main(String[] args) {

                        SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);

                        c1.sum();
                        c1.avg();

                        }

                        }
                        처음예제인데
                        ↓결과순서가 바꼇지만 되긴하네요
                        -10
                        30
                        15
                        대화보기
                        • selina
                          그러게요 저도 한 번 해봤는데 안되서.. ㅜㅜ 좀 궁금하네요
                          setOprands()를 꼭 해주어야하나요?
                          대화보기
                          • selina
                            완료
                          • 데이터정복
                            상속 완료!
                          버전 관리
                          egoing
                          현재 버전
                          선택 버전
                          graphittie 자세히 보기