성장일기

[Java] super와 super()

2022. 1. 29. 04:52

자바에서 super와 super()의 차이는 this와 this()의 차이처럼 완전히 다른 개념이다.

 

[Java] this()와 this

자바에서 this()와 this는 같은 키워드를 띄고 있지만 엄연히 다른 문법이다. this() 자바에서 같은 클래스의 멤버(변수, 메소드)간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능하

heesangstudynote.tistory.com

 

super

 super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다. 인스턴스 내에서 멤버변수와 지역변수의 이름이 같을 때 this키워드로 구별했듯이 상속받은 멤버(조상)와 자신의 멤버(자손)의 변수이름이 같을 때는 super를 붙여서 구별할 수 있다. 

 

 조상의 멤버와 자신의 멤버를 구별하는데 사용되는 점 빼고는 super와 this는 근본적으로 같다(같은 인스턴스 참조값을 가지고 있다). 모든 인스턴스의 메소드에는 자신이 속한 인스턴스의 주소가 지역변수로 저장되는데, 이것이 참조변수인 this와 spuer 값이 된다. 

public class TestFree {
    public static void main(String[] args) {
        Child c = new Child();
        c.info();
    }
}

class Child{
    public void info() {
        System.out.println(this.toString());
        System.out.println(super.toString());
    }
}

실행결과:

코드를 살펴보면 상속을 받지 않은 Child클래스의 info()메소드는 this와 super의 정보를 출력한다. 출력 결과를 보면 this와 super는 해당 인스턴스의 동일한 참조값을 가지고 있다. this와 super가 서로 다른 인스턴스의 참조값을 가지고 있는게 아니라 this는 단지 인스턴스변수와 지역변수의 이름이 같을 때를 위해, super는 조상클래스로 부터 상속받은 멤버와 자손의 멤버의 이름이 같을 때 이를 구별하기 위해 존재한다. 

 

 

 static메소드(클래스 메소드)는 인스턴스와 관련이 없다. 따라서 this와 마찬가지로 super역시 static메소드에서는 사용할 수 없고 인스턴스메소드에서는 사용할 수 없고 인스턴스메소드에서만 사용할 수 있다.

 

 

예제코드1

public class TestFree {
    public static void main(String[] args) {
        Child c = new Child();
        c.info();
    }
}

class Parent{
    int x = 10;
}

class Child extends Parent {

    void info() {
        System.out.println(x);
        System.out.println(this.x);
        System.out.println(super.x);
    }
}

실행결과:

위의 코드는 멤버x가 조상 클래스에만 존재하므로 x, this.x, super.x 모두 동일하게 10을 출력한다.

 

 

public class TestFree {
    public static void main(String[] args) {
        Child c = new Child();
        c.info();
    }
}

class Parent{
    int x = 10;
}

class Child extends Parent {
    int x = 20;

    void info() {
        System.out.println(x);
        System.out.println(this.x);
        System.out.println(super.x);
    }
}

실행결과:

다음의 경우에는 조상클래스의 멤버와 자손클래스의 멤버로 인스턴스 변수x가 동일한 이름으로 중복되어있다. 실행결과를 보면 x와 this.x는 자손클래스의 멤버변수를 출력하고 있고, super.x는 조상 클래스로부터 상속받은 멤버변수 x를 출력하고 있다. 

 

 이처럼 조상 클래스와 자손 클래스에 같은 이름의 멤버변수를 정의하는 것이 가능하며 참조변수 super를 이용하여 서로 구별할 수 있다.

 

 

이는 변수 뿐만이 아니라 메소드 역시 super를 써서 호출할 수 있다. 특히 조상 클래스의 메소드를 자손 클래스에서 오버라이딩한 경우에 super를 사용한다.

 아래 코드를 보면 getLocation()메소드는 좌표를 반환하는 메소드이다. Point3D클래스는 Point클래스를 상속받아서 x, y뿐만 아니라 z를 멤버로 가지고 있다. 조상 클래스의 getLocation()함수는 좌표x, y를 반환하는 메소드로 Point3D에서는 z좌표 1개가 더 추가되었으므로 좌표를 반환하는 메소드를 오버라이딩 해줘야할 필요가 있다. 이때 getLocation()메소드를 처음부터 다시작성하는 것이 아니라 super.getLocation()을 이용하여 조상클래스의 메소드를 호출하여 재정의할 수 있다.

 

 즉 조상클래스의 메소드의 내용에 추가적으로 작업을 덧 붙이는 경우에 super를 사용하여 조상클래스의 메소드를 포함시키는 것이 좋다. 후에 조상클래스의 메소드가 변경되더라도 변경된 내용이 자손클래스의 메소드에 자동적으로 반영이 되므로 객체지향적인 방법이다.

class Point{
    int x;
    int y;

    String getLocation() {
        return "x : " + x + ", y = " + y;
    }

}

class Point3D extends Point {
    int z;

    String getLocation() {
        return super.getLocation() + ", z : " + z;
    }
}

 

 

super()

 this()와 마찬가지로 super()역시 생성자와 관련이 있다. this()의 경우는 같은 클래스의 다른 생성자를 호출하는데 사용되지만, super()는 해당 클래스에서 조상 클래스의 생성자를 호출하는데 사용된다.

 

 자손 클래스의 인스턴스를 생성하면, 자손의 멤버와 조상의 멤버가 모두 합쳐진 하나의 인스턴스가 생성된다. 그래서 자손 클래스의 인스턴스가 조상 클래스의 멤버들을 사용할 수 있는 것이다. 이 때 조상 클래스의 멤버들이 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다. 

 

 생성자의 첫 줄에서 조상클래스의 생성자를 호출해야하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상클래스의 멤버들이 먼저 초기화되어 있어야 하기 때문이다.

 이와 같은 조상 클래스의 생성자 호출은 클래스의 상속관계를 거슬러 올라가면서 계속 반복된다. 마지막으로 코든 클래스의 최고 조상인 Object클래스의 생성자인 Object()까지 가서야 끝이 난다.

 

 그래서 Object클래스를 제외한 모든 클래스의 생성자는 첮 줄에 반드시 자신의 다른 생성자 혹은 조상의 생성자를 호출해야 한다. 그렇지 않으면 컴파일러는 생성자의 첫 줄에 'super()'를 자동적으로 추가할 것이다.

 

 

예제코드2

public class TestFree {
    public static void main(String[] args) {
        Point3D p = new Point3D(1, 2, 3);
    }
}

class Point{
    int x;
    int y;

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

    String getLocation() {
        return "x : " + x + ", y = " + y;
    }

}

class Point3D extends Point {
    int z;

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

    String getLocation() {
        return super.getLocation() + ", z : " + z;
    }
}

 위 예제를 컴파일하면 에러가 발생할 것이다. 왜냐하면 Point3D클래스의 생성자에서 클래스 내의 다른 생성자를 호출하지 않기 때문에 컴파일러는 'super()'를 여기에 삽입한다. super()는 조상클래스인 Point클래스의 기본 생성자인 Point()를 의미한다. 하지만 Point클래스에는 Point(int x, int y)와 같은 생성자를 정의하였으므로 기본 생성자인 Point()가 존재하지 않아서 발생하는 에러이다.

(+ 생성자가 정의되어 있는 클래스에는 컴파일러가 자동적을 기본 생성자 - Point()를 추가하지 않는다.)

 

 

 따라서 이 에러를 잡으려면 Point클래스 내에 생성자 Point()를 추가해주던가, Point3D(int x, int y, int z)생성자에서 super()를 이용하여 조상클래스의 생성자에서 멤버들이 초기화 될 수 있도록 해주는 방법이 있다.

public class TestFree {
    public static void main(String[] args) {
        Point3D p = new Point3D(1, 2, 3);
    }
}

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

    String getLocation() {
        return "x : " + x + ", y = " + y;
    }

}

class Point3D extends Point {
    int z;

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

    String getLocation() {
        return super.getLocation() + ", z : " + z;
    }
}

위와 같이 변경하면 된다. 조상 클래스의 멤버변수는 이처럼 조상의 생성자에서 초기화 하도록 하는 것이 바람직하다.

 

 

 

통합 예제코드

public class TestFree {
    public static void main(String[] args) {
        Point3D p1 = new Point3D();
    }
}

class Point{
    int x;
    int y;

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

    String getLocation() {
        return "x : " + x + ", y = " + y;
    }

}

class Point3D extends Point {
    int z;

    Point3D() {
        this(10, 20, 30);
    }

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

    String getLocation() {
        return super.getLocation() + ", z : " + z;
    }
}

지금까지 과정을 응용한 통합 예제이다. Point3D클래스의 인스턴스를 하나 생성했을 때 호출되는 생성자의 시점은 다음과 같다.

Point3D()  ->  Point3D(int x, int y, int z)  ->  super(x, y) = Point(int x, int y)  ->  super() = Object()

'Java' 카테고리의 다른 글

[Java] 제어자(modifier) - static, final, abstract  (0) 2022.02.06
[Java] 초기화 블럭(initialization block)  (0) 2022.01.24
[Java] this()와 this  (0) 2022.01.23
[Java] 가변인자(varargs)와 오버로딩  (0) 2022.01.07
[Java] 가변 배열  (0) 2022.01.06

공유하기

facebook twitter kakaoTalk kakaostory naver band