Java

상속과 생성자

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

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

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

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

1
2
3
4
5
6
7
package org.opentutorials.javatutorials.Inheritance.example4;
public class ConstructorDemo {
public ConstructorDemo(int param1) {}
public static void main(String[] args) {
ConstructorDemo c = new ConstructorDemo();
}
}

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

1
2
3
4
5
6
7
8
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의 값을 설정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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();
}
}

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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();
}
}

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

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

1
2
3
4
5
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의 생성자가 존재하지 않는다는 의미다. 하위 클래스가 호출될 때 자동으로 상위 클래스의 기본 생성자를 호출하게 된다. 그런데 상위 클래스에 매개변수가 있는 생성자가 있다면 자바는 자동으로 상위 클래스의 기본 생성자를 만들어주지 않는다. 따라서 존재하지 않는 생성자를 호출하게 되기 때문에 에러가 발생했다. 이 문제를 해결하기 위해서는 아래와 같이 상위 클래스에 기본 생성자를 추가하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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의 사용법을 알아보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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. 24.01.19 완료
  2. 서달
    20230323
  3. wwwqiqi
    완료
  4. infernist
    2022/10/31
  5. MelonMusk
    09/04
  6. 람보
    2022.9.1
  7. 치키티타
    220810
  8. PassionOfStudy
    복습 2일차!
  9. 치키티타
    220620 R
  10. PassionOfStudy
    상속과 생성자!
  11. 김은희
    220601 완료
    super 하위 클래스가 상위 클래스의 생성자 초기화
    부모가 먼저 초기화 그다음에 자식
  12. 자바잡아
    22.05.03 1회 완료
  13. 20220426
  14. aesop0207
    220329 Tue
  15. 모찌말랑카우
    22.02.15 완료
  16. aesop0207
    220207 Mon.
  17. 행달
    22.02.05 완료!
  18. 드림보이
    2021.12.08. 상속과 생성자 파트 수강완료
  19. syh712
    2021-12-06
    매개변수가 있는 생성자가 있을 때는 자동으로 기본 생성자를 만들어주지 않는다. 따라서 위의 예제는 존재하지 않는 생성자를 호출하고 있다. 이 문제를 해결하기 위해서는 아래와 같이 기본 생성자를 추가해줘야 한다.

    하위 클래스가 호출될 때 자동으로 상위 클래스의 기본 생성자를 호출하게 된다. 그런데 상위 클래스에 매개변수가 있는 생성자가 있다면 자바는 자동으로 상위 클래스의 기본 생성자를 만들어주지 않는다. 따라서 존재하지 않는 생성자를 호출하게 되기 때문에 에러가 발생한다. 이 문제를 해결하기 위해서는 상위 클래스에 기본 생성자를 추가하면 된다.
    상위 클래스인 Calculator에는 left와 right 값을 초기화할 수 있는 좋은 생성자가 이미 존재한다. 이것을 사용한다면 하위 클래스에서 left와 right의 값을 직접 설정하는 불필요한 절차를 생략할 수 있을 것이다.
    super는 상위 클래스를 가리키는 키워드다.
    super 키워드는 부모 클래스를 의미한다. 여기에 ()붙이면 부모 클래스의 생성자를 의미하게 된다. 이렇게 하면 부모 클래스의 기본 생성자가 없어져도 오류가 발생하지 않는다.
    하위 클래스의 생성자에서 super를 사용할 때 주의할 점은 super가 가장 먼저 나타나야 한다는 점이다. 즉 부모가 초기화되기 전에 자식이 초기화되는 일을 방지하기 위한 정책이라고 생각하자.
  20. IaaS
    2021-11-01 수강완료
  21. 성치
    2021-11-01 완료
  22. H4PPY
    1017
  23. 미NI언
    10.11 완료!!
  24. 베이스박
    210825 학습완료.감사합니다.
  25. super1Nova
    210820
  26. 이땅콩
    다른 것은 다 상속받되, 사용할 parameter들을 따로 사용하려면
    자식 클래스에서 부모 클래스의 생성자를 부르면 되는 군요
    엄마가 사주는 지우개랑 내가 사는 지우개는 본질적으로 다르므로,
    "엄마 돈 줘 내가 super가서 알아서 살게"
    이렇게? 저급하고 누추하지만 제 나름대로 묘사를 들어봤습니다.
  27. 김수빈
    부모클래스
    자식클래스

    부모클래스를 상속받은 자식클래스가
    인스턴스를 생성할때
    자식클래스의 생성자와 부모클래스의 생성자를 호출하게 돼있음.

    근데 이때 자바에서는 기본값으로 기본 생성자를 호출하려고 함
    (그래서 매개변수가 존재하는 생성자가 있을경우 오류가 남)

    그래서 아예 자식클래스를 생성할때 사용자가 임의적으로
    부모클래스(매개변수가 있는)의 생성자를 호출해줌으로써 실행이 됨.
  28. 악어수장
    2021-5-13
  29. 임태근
    쉽게 생각하면 자식 클래스에서 상속,생성자,super 셋트로 선언해주면 만사 오케이라고 이해해도 되나용?

    부모클래스의 기능을 받고(상속)

    생성자 선언해주고(부모기능 받아오고 다른 기능을 써야하니까)

    super를 이용해 부모클래스 위에 기본생성자 생성 안 했을 때의 상황까지 고려.
  30. 하연주
    210207 완료
  31. 김태현
    2차 수업완료
  32. hvii
    20200811
  33. yulrinam
    200810 시청완료 감사합니다 :)
  34. EunSeok Kang
    잘봤습니다. 이동하며 봐서 제대로 이해가 안되어 다시 봅니다.(졸았음)
  35. RooT
    SubstractionableCalculator 클래스의 부모클래스인 Calculator에서 따로 생성자를 호출하시지 않으셨기 때문에
    Java에서 생성자를 따로 명시하지 않으면 자동으로 기본생성자를 호출 및 초기화가 이루어진다고
    이고잉선생님께서 말씀해주셨죠? 그런 경우에 해당됩니다.
    그리고 상속관계이기 때문에 부모 클래스의 메소드들을 문제없이 호출하여 사용할 수 있는거구요
    대화보기
    • RooT
      SubstractionableCalculator 클래스의 부모클래스인 Calculator 클래스의 생성자를
      super를 이용해서 호출하셨기 때문에 Calculator는 필요가 없는겁니다.
      super(left,right); 라고 인자를 전달해서 부모클래스의 생성자를 호출하면
      매개변수가 포함되어있는 형식의 생성자인 Calculator(int left, int right){}가 실행이 되기 때문에
      따로 Calculator(){} 즉, 기본생성자는 필요가 없어지는거죠
      대화보기
      • David YJ Lee
        public Caculator() {} //이게 왜 필요한걸까요 ?? 없어도 되는데...

        아래는 전체 코드입니다.

        package org.opentutorials.javatutorials.Inheritance.example2;

        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 CalculatorConstructorDemo4 {
        public static void main(String[] args) {
        SubstractionableCalculator c1 = new SubstractionableCalculator(10, 20);
        c1.sum();
        c1.avg();
        c1.substract();
        }
        }
      • David YJ Lee
        왜 이렇게 하면 안되는 건가요 ? 에러없이 잘 돌아가는 코드입니다.

        package org.opentutorials.javatutorials.Inheritance.example2;

        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;
        }

        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();
        }
        }
      • 수환잉
        완 료
      • bomnie
        클래스를 객체화시킬 때 사용하는 new 키워드는 실제로는 생성자를 호출하는 것이다.
        생성자를 코드로 정의해 주지 않으면 자바는 기본적으로 아무런 파라미터를 가지지 않는 기본 생성자를 만들어 준다.
        반대로 생성자를 따로 정의해주면 자바는 기본 생성자를 만들지 않는다.
      • 김승민
        2020-04-10
        잘 들었습니다~
        일단 다음으로...
      • yuji
        2020-3-8 완료
      • 문바이
        this : Instance의 소속에 접근할때 사용하는 접근자. (클래스 하고는 무관함)
        super : 내 클랙스의 상위 Class를 의미함.
        super( ) : 상위 Class의 Construct Method와 Class이름이 동일하므로 이 method가 호출됨.
        super.setOprands( ) : 상위 Class의 method를 호출함.

        생성자는 최상위부터 순차적으로 자동실행됨.

        class C1{
        public C1() {
        System.out.println("C1");
        }
        }

        class C2 extends C1{
        public C2() {
        System.out.println("C2");
        }
        }

        class C3 extends C2{
        public C3() {
        System.out.println("C3");
        }

        }
        public class HelloWorldApp {
        public static void main(String[] args) {
        C3 a = new C3();
        }
        }

        >>결론
        C1
        C2
        C3
        대화보기
        • aminora
          쉽게 생각하면 상속을 받은 자식클래스에서 메서드를 실행할때 상속한 부모클래스쪽에 다이렉트로 초기화를 위해서는 this. 대신 super.를 사용하라는 거네요, 아니면 위쪽 예제처럼 부모 클래스쪽에서도 초기화를 위한 this.가 포함된 메서드를 추가하거나... 그냥 super쓰는게 좋겠네요.
          this. -> 클래스 내부의 메서드에서 소속된 클래스 변수를 호출 및 연결
          super. -> 부모클래스 내부의 메서드에서 소속된 클래스 변수를 호출 및 연결
        • 지니
          생성자 역할중 하나는 클래스 필드를 초기화 하는것인데, 클래스 생성시 반드시 초기화 하여야 할 필드가 있다면 기본 생성자가 있으면 안되겠죠. 해당 필드를 초기화 하지 못하기 때문에..
          대화보기
          • 이채
            인자가 있는 생성자를 정의한 경우에 자바에서 기본생성자를 안 만들어주는 이유가 혹시 따로 있나요??
            항산 자동으로 기본생성자을 만들어주면 좋을 것 같은데..
          • 허공
            감사합니다!
          • ㅇㅇ
            작성자입니다.
            상속받은 클래스가 부모클래스에 매개변수가 전달되지않았을때
            상속받은 클래스에만 매개변수를 전달해야하는데 되지않기때문에
            부모클래스에 기본생성자를 만들어주는것이 맞죠?
            대화보기
            • ㅇㅇ
              매개변수가 있는 생성자가 public Test(int ~~){}인거고
              기본 생성자가 public Test(){}인거죠?
              매개변수가 있는 생성자를 만드려면
              기본생성자도 같이 만들어야하는거고
            • PassionOfStudy
              191002(수) - (4)
              수강완료~
            버전 관리
            egoing
            현재 버전
            선택 버전
            공동공부
            graphittie 자세히 보기