코린이의 소소한 공부노트

생성자 super() vs 참조변수 super 본문

Java

생성자 super() vs 참조변수 super

무지맘 2022. 3. 23. 22:57

이 글을 읽기 전 다음 링크를 읽었으면 좋겠다!

2022.03.12 - [Java] - 생성자 this() vs 참조변수 this

-> 바쁜 사람들을 위한 요약본

1) 생성자 this()는 같은 클래스 내의 다른 생성자를 호출하기 위한 것

2) 참조변수 this는 인스턴스 자신을 가리키는 참조변수. 생성자, im 내부에서 lv와 iv 구별 위해 사용. sm에서 사용 불가

 

생성자 super()와 참조변수 super도 이름만 같을 뿐, 하는 일은 완전히 다르다.

 

[참조변수 super]

1. 객체의 조상 클래스를 가리키는 참조변수이다.

  - 객체 자신을 가리키는 this와 차이가 있다.

  - 더 구체적으로 설명하자면, 조상 클래스로부터 상속받아 자신의 멤버가 된 것을 가리킨다.

    -> 넓은 의미로 객체 자신을 가리킨다고 볼 수 있다.

2. im, 생성자 내에만 사용 가능하다

  - sm에서 사용할 수 없다.

  - this도 마찬가지다.

3. 조상의 멤버와 자신의 멤버를 구별할 때 사용한다.

  - 조상의 멤버와 자신의 멤버의 이름이 같을 때, 조상으로부터 상속받은 멤버 앞에 super를 붙여서 구분한다.

  - this의 경우, lv과 iv의 이름이 같을 때 iv 앞에 this를 붙인다.

예시1) 부모와 자손 클래스에 똑같은 이름의 멤버 변수 x가 있을 때

class Parent { int x = 10; } // super.x

class Child extends Parent {
    int x = 20; // this.x

    void method() {
        System.out.println("x = " + x);
        System.out.println("this.x = " + this.x);
        System.out.println("super.x = "+ super.x);
    }
}

// main 메서드 내부
Child c = new Child();
c.method();
// "x = 20"
// "this.x = 20"
// "super.x = 10"

예시2) 부모의 멤버 변수를 상속받고, 자식의 멤버 변수는 없을 때

class Parent2 { int x = 10; }

class Child2 extends Parent2 {
    void method() {
        System.out.println("x = " + x);
        System.out.println("this.x = " + this.x);
        System.out.println("super.x = "+ super.x);
    }
}

// main 메서드 내부
Child2 c = new Child2();
c.method();
// "x = 10"
// "this.x = 10"
// "super.x = 10"

두 가지의 가장 큰 차이점은 자손 클래스의 멤버의 개수 차이다.

  - 첫 번째 코드의 자손: 부모1 + 자식2 = 3개의 멤버

  - 두 번째 코드의 자손: 부모1 + 자식1 = 2개의 멤버

그래서 자손 클래스를 이용해 객체를 만들면 아래 그림과 같이 메모리상에 객체가 생성된다.

 

[생성자 super()]

1. 조상의 생성자를 호출할 때 사용한다.

  - 같은 클래스 내의 생성자를 호출하는 this()와 차이가 있음

2. 조상의 클래스 이름 대신 super를 사용한다.

  - 물론, 매개변수의 타입과 개수를 맞춰서 사용해야 함

3. 조상의 멤버는 조상의 생성자를 호출해서 초기화하는 것이 좋다.

  - 상속받은 멤버는 super()를 이용해 조상에게 초기화를 맡기자

  - 상속받는 목록에 생성자와 초기화 블록은 제외됨

Point 클래스와 이를 상속받은 Point3D 클래스가 있다.

class Point {
    int x, y;

    Point(int x, int y) { // iv 초기화
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point {
    int z;

    Point3D(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    } // x, y는 조상의 iv
}

자손의 생성자에서 조상의 iv인 x, y를 초기화하고 있다. 에러가 나지는 않으나, 가급적이면 자신이 직접 선언한 것만 초기화하는 것이 좋다. 때문에 Point3D의 생성자를 아래와 같이 바꾸는 것이 좋다.

Point3D(int x, int y, int z) {
    super(x, y) // 조상의 생성자 Point(int x, int y) 호출
    this.z = z;
}

 

[생성자 작성 시 유의사항]

생성자에 대한 기본 설명은 아래 링크된 글에 자세히 적혀있다.

2022.03.12 - [Java] - 생성자

생성자를 만들 때 반드시 첫 줄에서 생성자를 호출해야 한다. 그렇지 않으면 컴파일러가 첫 줄에 super();를 삽입한다.

class Point {
    int x, y;

    Point(){ // 1번
        this(0, 0); // 2번 생성자 호출
    }

    Point(int x, int y) { // 2번
        this.x = x;
        this.y = y;
    }
}

Point 클래스에는 2개의 생성자가 있다.

1번: 같은 클래스 내에 있는 2번 생성자 호출 -> ok

2번: 첫 줄에 생성자 호출X -> 컴파일러가 super(); 삽입

위 코드를 실행시키면 컴파일 단계에서 코드가 아래와 같이 바뀐다.

 

class Point extends Object { // 컴파일 후 변경된 부분 1
    int x, y;

    Point(){
        this(0, 0);
    }

    Point(int x, int y) {
        super(); // Object();와 같음 -> 컴파일 후 변경된 부분 2
        this.x = x;
        this.y = y;
    }
}

이번에는 부모, 자손 클래스를 만든 후 자손 클래스에서 생성자를 만들어보자.

class Point{ // 2차원 좌표
    int x;
    int y;

    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point{ // 3차원 좌표
    int z;

    Point3D(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

// main 메서드 내부
Point3D p = new Point3D(1, 3, 5); // error: Implicit super constructor Point() is undefined

부모 클래스에는 매개변수가 2개인 생성자가 있고, 자손 클래스에는 매개변수가 3개인 생성자가 있다. 잘 만든 거 같은데, 메인에서 자손 클래스의 객체를 생성하려니 에러가 발생했다..! 왜 에러가 난 것인지 컴파일러 입장에서 살펴보겠다.

-> 두 클래스 모두 생성자가 있네? 그럼 기본 생성자는 추가해주지 않아도 되겠다.

-> 두 클래스 모두 생성자에서 첫 줄에 생성자 호출을 하지 않았다. 까먹지 말라니까.. 그래서 super();를 자동으로 추가해주기로 했다. 이건 내가 해줄 수 있는 부분이니 에러의 이유는 여기가 아닌데..

// Point 클래스
Point(int x, int y){
    super(); // 추가!
    this.x = x;
    this.y = y;
}

// Poin3D 클래스
Point3D(int x, int y, int z){
    super(); // 추가!
    this.x = x;
    this.y = y;
    this.z = z;
}

-> 메인으로 가보자. 매개변수가 3개인 Point3D 객체를 생성했네? 그럼 매개변수가 3개인 생성자를 호출해야지.

-> Point3D 클래스의 생성자를 보니 첫 줄에 내가 추가해준 super();가 있네. 그럼 부모 클래스의 기본 생성자를 호출해야겠다.

-> Point 클래스를 살펴보니 매개변수가 0개인 기본 생성자는 없는데..? 생성자가 있길래 기본 생성자는 안 만들어 줬는데.. 에이 모르겠다 사용자가 알아서 하겠지! 에러!! 에러!!

-> error: constructor Point() is undefined

 

위의 에러를 해결하는 방법은 2가지 정도로 볼 수 있다.

1. 조상 클래스에 기본 생성자 추가

class Point{ // 2차원 좌표
    int x;
    int y;

    Point() {} // 변경된 부분

    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point{ // 3차원 좌표
    int z;

    Point3D(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
    }
}

2. super()를 이용해서 조상 클래스의 생성자 호출

class Point{ // 2차원 좌표
    int x;
    int y;

    Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point{ // 3차원 좌표
    int z;

    Point3D(int x, int y, int z){
        super(x, y); // 변경된 부분
        this.z = z;
    }
}

x, y는 조상 클래스로부터 상속받은 멤버이므로, 2번이 더 좋은 것이라는 생각이 든다.

 

앞으로 클래스를 생성할 때 이 두 가지를 까먹지 말자.

  - 기본 생성자 필수

  - 생성자 첫 줄에 생성자 호출 필수

'Java' 카테고리의 다른 글

임포트와 스태틱 임포트  (0) 2022.04.14
패키지와 클래스 경로  (0) 2022.04.13
메서드 오버라이딩 (+오버로딩과의 차이점)  (0) 2022.03.22
클래스 간의 관계 - 상속, 포함  (0) 2022.03.15
변수의 초기화  (0) 2022.03.12