Java

상속과 생성자

편리함을 위해서 어떠한 기능을 수용하면 그 기능이 기존의 체계와 관계하면서 다양한 문제를 발생시킨다. 그 문제를 한마디로 줄여서 말하면 복잡도의 증가라고 할 수 있다. 이번 시간에는 생성자가 상속을 만나면서 발생한 복잡성을 보여줄 생각이다. 그 맥락에서 super이라는 키워드의 의미도 중요하게 다뤄질 내용이다. 

이번 수업을 이해하기 위해서는 기본 생성자의 성질에 대한 이해가 선행되야 한다. 아래의 예제를 보자.

package org.opentutorials.javatutorials.Inheritance.example4;
public class ConstructorDemo {
	public static void main(String[] args) {
		ConstructorDemo  c = new ConstructorDemo();
	}
}

위의 예제는 에러를 발생시키지 않는다. ConstructorDemo 객체를 생성할 때 자동으로 생성자를 만들어주기 때문이다. 하지만 아래의 예제는 에러가 발생한다.

package org.opentutorials.javatutorials.Inheritance.example4;
public class ConstructorDemo {
    public ConstructorDemo(int param1) {}
	public static void main(String[] args) {
		ConstructorDemo  c = new ConstructorDemo();
	}
}

매개변수가 있는 생성자가 있을 때는 자동으로 기본 생성자를 만들어주지 않는다. 따라서 위의 예제는 존재하지 않는 생성자를 호출하고 있다. 이 문제를 해결하기 위해서는 아래와 같이 기본 생성자를 추가해줘야 한다.

package org.opentutorials.javatutorials.Inheritance.example4;
public class ConstructorDemo {
    public ConstructorDemo(){}
	public ConstructorDemo(int param1) {}
	public static void main(String[] args) {
		ConstructorDemo  c = new ConstructorDemo();
	}
}

이제 본론으로 들어가보자. 상속 토픽의 첫 번째 예제를 조금 수정해서 생성자를 통해서 left, right의 값을 설정한다.

package org.opentutorials.javatutorials.Inheritance.example2;

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 SubstractionableCalculator(int left, int right) {
		this.left = left;
		this.right = right;
	}

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

public class CalculatorConstructorDemo4 {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

이해를 돕기 위해서 아래와 같이 차이점만을 부각한 이미지를 첨부하였다. 붉은색으로 표시된 부분이 달라진 부분이다.

실행결과는 아래와 같다.
30
15
-10

SubstractionableCalculator의 생성자로 left와 right의 값을 받아서 초기화시키고 있다. 만약 클래스 Calculator가 메소드 setOprands가 아니라 생성자를 통해서 left, right 값을 설정하도록 하고 싶다면 아래와 같이 코드를 변경해야 할 것이다.

package org.opentutorials.javatutorials.Inheritance.example3;

class Calculator {
    int left, right;
	
	public Calculator(int left, int right){
		this.left = left;
		this.right = 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 SubstractionableCalculator(int left, int right) {
		this.left = left;
		this.right = right;
	}

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

public class CalculatorConstructorDemo5 {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

달라진 부분은 아래와 같다.

위의 코드를 실행하면 오류가 발생한다. 오류의 내용은 아래와 같다.

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Implicit super constructor Calculator() is undefined. Must explicitly invoke another constructor

	at org.opentutorials.javatutorials.Inheritance.example3.SubstractionableCalculator.<init>(CalculatorConstructorDemo5.java:26)
	at org.opentutorials.javatutorials.Inheritance.example3.CalculatorConstructorDemo5.main(CalculatorConstructorDemo5.java:38)

즉 상위 클래스인 Calculator의 생성자가 존재하지 않는다는 의미다. 하위 클래스가 호출될 때 자동으로 상위 클래스의 기본 생성자를 호출하게 된다. 그런데 상위 클래스에 매개변수가 있는 생성자가 있다면 자바는 자동으로 상위 클래스의 기본 생성자를 만들어주지 않는다. 따라서 존재하지 않는 생성자를 호출하게 되기 때문에 에러가 발생했다. 이 문제를 해결하기 위해서는 아래와 같이 상위 클래스에 기본 생성자를 추가하면 된다.

package org.opentutorials.javatutorials.Inheritance.example3;

class Calculator {
    int left, right;
	
	public Calculator(){
		
	}
	
	public Calculator(int left, int right){
		this.left = left;
		this.right = 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 SubstractionableCalculator(int left, int right) {
		this.left = left;
		this.right = right;
	}

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

public class CalculatorConstructorDemo5 {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

차이점은 아래와 같다.

그런데 상위 클래스인 Calculator에는 left와 right 값을 초기화할 수 있는 좋은 생성자가 이미 존재한다. 이것을 사용한다면 하위 클래스에서 left와 right의 값을 직접 설정하는 불필요한 절차를 생략할 수 있을 것이다. 어떻게 하면 상위 클래스의 생성자를 호출할 수 있을까?

super

super는 상위 클래스를 가리키는 키워드다. 예제를 통해서 super의 사용법을 알아보자.

package org.opentutorials.javatutorials.Inheritance.example3;

class Calculator {
    int left, right;
	
	public Calculator(){}
	
	public Calculator(int left, int right){
		this.left = left;
		this.right = 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 SubstractionableCalculator(int left, int right) {
		super(left, right);
	}

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

public class CalculatorConstructorDemo5 {
	public static void main(String[] args) {
		SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
		c1.sum();
		c1.avg();
		c1.substract();
	}
}

차이점은 아래와 같다.

super 키워드는 부모 클래스를 의미한다. 여기에 ()붙이면 부모 클래스의 생성자를 의미하게 된다. 이렇게 하면 부모 클래스의 기본 생성자가 없어져도 오류가 발생하지 않는다.

하위 클래스의 생성자에서 super를 사용할 때 주의할 점은 super가 가장 먼저 나타나야 한다는 점이다. 즉 부모가 초기화되기 전에 자식이 초기화되는 일을 방지하기 위한 정책이라고 생각하자.

댓글

댓글 본문
작성자
비밀번호
  1. 기수니
    아, 다시 되세김질 해서 super() 재대로 확인 해보네요
  2. 김성연
    감사합니다
  3. 0.8009787737609289
    햇갈린다
  4. 백용빈
    SubstractionableCalculator가 Calculator를 상속하고 있기 때문입니다.
    대화보기
    • 김시진
      안녕하세요 정말 도움이 많이 되고있습니다 ㅜㅜ 제가 남들보다 이해력이 조금 부족해서 못따라가는 경향이 있지만 이 동영상 보고 정말 많은 도움이 되고있습니다 ㅜㅜ

      혹시 jsp강의는 안하시나요??ㅠㅠ
    • 전하연
      감사합니다
      2018.8.19 15:05
    • 뚱이
      뚱인데요
    • phoenix
      안녕하세요
    • 서호
      전역변수와 지역변수에 대해서 다시한번 봐보세요.
      대화보기
      • 유자
        메인함수에서 하위클래스 생성자 호출->
        하위클래스에서 상위클래스의 생성자를 호출
        (진홍님의 말대로 상위클래스의 초기화가 먼저 이루어져야합니다!
        상위클래스가 존재해야 하위클래스도 존재할 수 있기 때문이죠)->
        상위 클래스 생성자 작동(또는 변수 초기화)->
        하위클래스 생성자 작동->
        메인 함수로 다시 돌아옴

        뿌리를 찾으러 거슬러 올라갔다가 다시 내려오는 형식이라고 생각하시면 됩니다!
        그래서 super를 호출할 때 하위클래스 생성자 정의 부분에 가장 먼저 써주는 이유기도 하구요.
        제 방식대로 이해해서 혹시 틀린 부분이 있을 수 있지만...도움이 되었으면 좋겠습니다!
        대화보기
        • 이태호
          7/4
          super(left,right); 의 사용법
        • 멀캠A_Son
          완료
        • 진홍
          변수는 상위클래스에서 초기화가 되있습니다. 말그대로 하위클래스란것은 상위클래스 코드에다 추가한것이라고 생각하시면 된다고 생각합니다.
          대화보기
          • 유나
            class SubstractionableCalculator extends Calculator {
            public SubstractionableCalculator(int left, int right) {
            this.left = left;
            this.right = right;
            }
            }
            이 부분은 Class에 left, right라는 변수를 선언해주지 않았는데 왜 this를 쓰나요??
            변수를 선언하지않았는데 this.left = left 라는 말을 쓸 수 있는건가요?
          • B.tory
            생성자와 setOperand의 기능이 같아보여도 쓰임이 다르다고 생각합니다.
            밑에서 객체를 생성하고 싶을때
            Caculator c1 = new Caculator();
            Caculator c2 = new Caculator(5,3);
            이렇게 생성된다하면 c1에서는 setOparand메서드가 있어야 두 변수를 추가 할 수 있습니다.
            즉, 객체를 생성하는 동시에 변수를 넣는 것이 아닌 후에 추가하고 싶은 경우 setOprand메서드가 있어야 가능하다는 것입니다.
            대화보기
            • ㅎㅇ
              생활코딩님의 설명이랑 저의 이해력이랑 너무안맞는거같아요ㅠㅠ
              생활코딩님 설명듣고 이해가안되서
              제가 스스로 계속 프로그래밍하면서
              꺠달았네요 ㅠㅠ
            • ㅎㅇ
              생활코딩님의 설명이랑 저의 이해력이랑 너무안맞는거같아요ㅠㅠ
              생활코딩님 설명듣고 이해가안되서
              제가 스스로 계속 프로그래밍하면서
              꺠달았네요 ㅠㅠ
            • 지나가는사람
              public Calculator(int left, int right){
              this.left = left;
              this.right = right;
              }

              이건 클래스의 생성자 입니다.
              생성자를 배우기 전에 setOperand라는 메소드를 만들어서 left, right값을 쓴건데요.
              생성자를 배웠기 때문에 저 setOperand를 없애도 무방하죠.
              생성자를 없애는 것보다 setOperand를 없애는 것이 더 좋습니다.
              대화보기
              • 아빠가간다.
                두세번 보고 이해했습니다. 아니 깨달았다고 해야하나...
                super 메소드를 설명하기 위해 좀 억지로 돌아온 느낌이 있네요...
                조금 이해하기 어려웠습니다.
              • 열정하나
                음 좀더 다시 보고 공부해야겠다.
              • 5월 2일 상속과 생성자.
                하위클래스에 생성자가 있어 근데 상위클래스에 기본 생성자가 없어 그러면 에러가 난다. 하위클래스의 생성자가 실행되기 위해서는 상위클래스의 기본생성자가 실행되어야 한다. 만약 상위클래스의 기본생성자를 만들지 않고 사용하려면 하위클래스 생성자에 super키워드를 이용하여 상위클래스의 생성자를 초기화한 후에 하위클래스의 생성자 초기화를 진행하도록 해야한다.

                이 개념이 맞는건가요?
              • 박현모
                감사합니다~ 잘 봤습니다~
              • 황금백수
                최고입니다
                핵심을 짚어주시네요 ㅎㅎ
              • 돌침대에서덤블링
                setOperands 함수 호출 없이 객체 생성시 left 와 right 값을 설정하기 위해서 생성자를 하나 더 만든겁니다.
                대화보기
                • 하면된다하자
                  완료
                • ㅇㄹㅇㄹ
                  맨 아래 예제에서 8~11행 코드의 용도는 뭔가요??

                  public Calculator(int left, int right){
                  this.left = left;
                  this.right = right;
                  }
                  이것.

                  밑에 셋 오프랜즈 메소드가 있는데 그 위에다가 또 저걸 써주는 이유는 뭐죠??
                • popinbompin
                  complete
                • 데이터정복
                  생성자의 복잡성과 super의 용도 이해 완료!
                • Younghun Liam Youn
                  한번 강의 듣고는 이해가 잘 되지 않아서 '클래스 멤버와 인스턴스 멤버' 강의부터 다시 곱씹고 보니까 이해가 되네요! 혹시 저처럼 잘 이해가 안되시는 분들은 복습 한번 해보셔도 좋을 듯 합니다!
                • GoldPenguin
                  감사합니다.
                • zugi
                  완료.!
                • 김지현
                  질문1 : 두 객체 모두 매개변수가 있는 객체를 생성하는 것인데 왜 상속을 받은 클래스에는 기본 생성자가 반드시 있어야 하는지 궁금합니다.
                  - sub클래스 에서 super 키워드 사용시 super 클래스의 기본생성자가 필요없지만 , super키워드를 사용하지않을경우 super 클래스 에서 기본생성자가 있어야 합니다.

                  질문2 : 만약 첫 번째 경우 기본 생성자가 자동으로 생성된다면 두 번째의 경우는 왜 기본 생성자가 자동으로 생성되지 않는지 궁금합니다.
                  - 2번째의 경우
                  매개변수가 있는 생성자 를 생성할 경우
                  컴파일러가 컴파일시 생성자가 이미 있음으로 생성하지 않습니다.
                  사용자가 기본 생성자를 직접 추가 해야 오류가 생기지 않습니다.

                  기본 생성자가 자동으로 생성되는 경우는
                  매개변수 가 있는 생성자가 없을경우 컴파일러 가 생성자가 없음을 확인하고 컴파일시 자동으로 생성합니다.
                  대화보기
                  • 상속이랑 연결되다 보니 참 와닿기 힘든 개념인듯...ㅠ
                  • seokmin2004
                    완료
                  • C언어만마스터
                    완료
                  • yoon88
                    완료/ 다시보기
                  • darkoz89
                    강의 수강중 질문있습니다!!

                    초기화와 생성자 강의부분에서
                    24번 째줄
                    Calculator c1 = new Calculator(10, 20);
                    객체를 생성 시킬 때는
                    6번째 줄
                    public Calculator(int left, int right) {
                    this.left = left;
                    this.right = right;
                    }
                    과 같이 기본 생성자 없이 매개 변수가 있는 생성자만 호출 시켜도 가능합니다. 그러나
                    상속과 생성자 강의
                    중간인 38번째 줄
                    SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
                    객체를 생성하기 위해서는 기본생성자가 필요하여 기본생성자를 생성하거나 super를 써서 기본생성자를 만들었습니다.

                    질문1 : 두 객체 모두 매개변수가 있는 객체를 생성하는 것인데 왜 상속을 받은 클래스에는 기본 생성자가 반드시 있어야 하는지 궁금합니다.
                    질문2 : 만약 첫 번째 경우 기본 생성자가 자동으로 생성된다면 두 번째의 경우는 왜 기본 생성자가 자동으로 생성되지 않는지 궁금합니다.
                  • gomu92
                    저는 여기부터 막히네요 ㅠㅠ 힘!!
                  • Keehwan Jee
                    설명 잘하셨네요. 감사합니다
                    대화보기
                    • 미림_likelion
                      수강 완료했습니다. 감사합니다.
                    • nakra
                      생성자를 정의한 클래스를 상속받기 위해서는
                      부모 클래스에 기본 생성자를 넣어주거나
                      자식 클래스에 슈퍼클래스를 이용한다는 개념이 맞나요?


                      이 경우 부모 클래스에 기본 생성자가 있어야 이유는
                      부모 클래스에서 초기화를 담당하던 생성자를 무력화시키고,
                      자식 클래스에서 초기화를 담당하는 생성자만 작동하게 하기 위함인거죠?
                    • J_Project
                      감사합니다!
                    • 꿈돌이
                      부모클래스의 기본생성자가 애초에 없고 그 부모클래스코드가 수정이 어렵거나 안될때(다른개발자가만든코드)
                      자식클래스에서 매개변수가 있는 생성자를 좀 만들어주고싶은데 부모클래스에 수정이 어려우니 자식이 super()를 통해서 부모클래스에 기본생성자가 있는것으로 나타낸다는말아닌가요??
                    • 김인섭
                      감사합니다.
                    • 궁그미
                      마지막 예제에 하위 클래스 생성자

                      class SubstractionableCalculator extends Calculator {
                      public SubstractionableCalculator(int left, int right) {
                      super(left, right);
                      }

                      여기에서 super();가 들어간다면
                      상위 클래스에서 기본 생성자인
                      public Calculator(){}를
                      따로 생성하지 않아도 된다는 말이죠?

                      class Calculator {
                      int left, right;

                      // public Calculator(){}

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

                      하위 클래스의 생성자에 super();가 없으면
                      상위 클래스의 기본 생성자를 만들어줘야 하고요.
                      제가 제대로 이해한 것이 맞나 궁금합니다.
                    • YoungWoong Ha
                      http://ideone.com/u15zAI

                      부모클래스의 기본 생성자를 생성해준다는 건 알겠는데
                      자녀클래스인 SubstractionableCalculator에서 입력한 값이 부모클래스에게까지 전달되서 sum, avg까지 결과값이 나온다는 건 이해가 안되네요.

                      또 아래 부분은 삭제되어도 관계 없을것 같네요.
                      public void setOprands(int left, int right) {
                      this.left = left;
                      this.right = right;
                      }
                    • 진격의숑숑
                      감사합니다
                    • 라떼
                      어렵지만 복습을 통해 이해해야겠어요 ㅠ 감사합니다!
                    • jimin_park(by.herO)
                      아래 제 설명을 결론으로 도출시켜 보자면
                      자식 클래스의 기본 생성자는 어떻게 될까요?

                      public subclass(){

                      super(){}

                      }

                      아마 이렇게 되있겠죠 ? 이걸 머리로써 이해하신다면 이번 수업은 전부 이해하신겁니다.
                      대화보기
                      • jimin_park(by.herO)
                        어쩌면 자바에서 가장 어려운 개념이 바로 이 개념이 아닐까 사료됩니다.
                        제가 아는 선에서 여러분들이 의아해 하시는 사항을 논리적으로 알려드릴까 합니다.
                        자식클래스 SubstractionableCalculator로 객체(인스턴스)를 생성할 때
                        자기자신 SubstractionableCalculator의 생성자를 이용하게되고 이 생성자안에
                        부모클래스Calculator의 생성자가 암시적(implicit)으로 설정되어있어
                        부모클래스의 인스턴스 변수를 초기화 해줍니다. 그것이 super(); 이고
                        부모클래스에 default constructor(기본 생성자)가 없다면 그래서 에러가 발생 하는 것이죠.
                        하지만 자식클래스 생성자안에 Super(parameter)를 따로 적어주면 부모클래스의 생성자를 호출할 때
                        기본생성자가 아닌 Super(parameter)생성자로 부모클래스 인스턴스 변수를 초기화하게 되고
                        그렇기 떄문에 부모클래스에 default생성자가 명시되어있지 않아도 에러가 발생하지 않는 것이죠.

                        *설명이 장황한데 요점만 발췌한다면, 자식클래스로 객체를 생성할 떄 자식클래스 생성자를 통해
                        객체가 생성이 되는데 이 생성자안에 부모클래스의 생성자가 묵시적 혹은 명시적으로
                        선언되어 부모클래스의 인스턴스 변수들을 초기화하고
                        그 나머지 자식클래스만이 가지는 인스턴스 변수들을 초기화 해준다 입니다.
                        어찌보면 클래스라는 설계도 2개로 하나의 객체를 만들기 때문에 발생하는 복잡성이죠
                      버전 관리
                      egoing
                      현재 버전
                      선택 버전
                      graphittie 자세히 보기