super란 자식 클래스에서 상속받은 부모 클래스의 멤버를 참조하는데 사용되는 참조변수이다.
아래와 같이 자동차를 동작시키는 클래스가 있다고 가정하자.
Car
클래스
public class Car {
boolean startupState;
float speed;
{// 초기화 블럭
this.startupState=false;
this.speed=0.0f;
}
void startUp() {// 시동걸기
this.startupState=true;
System.out.println("시동걸림.");
}
void drive(float speed) {// 이동
this.speed=speed;
System.out.println("속도: "+this.speed+"km");
}
void stop() {// 브레이크
this.speed=0.0f;
this.startupState=false;
System.out.println("시동끔.");
}
void horn() {// 경적
System.out.println("빵빵!! 피자빵!! 초코빵!! 크림빵!!");
}
}
그리고 이를 상속받아 슈퍼카를 동작시키는 클래스가 있다 가정하자.
SuperCar
클래스
public class SuperCar extends Car {
int gearStage;
int mode;
void manualGearShift(int stage) {// 기어 변속
this.gearStage=stage;
System.out.println("기어 단수: "+ this.gearStage);
}
void driveMode(int mode) {// 운전모드
this.mode=mode;
switch(mode) {
case 1:
System.out.println("운전모드: 스포츠");
break;
case 2:
System.out.println("운전모드: 컴포트");
break;
default:
System.out.println("운전모드: "+ this.mode);
break;
}
}
}
이때, 상속받은 슈퍼카 클래스에서 부모클래스인 Car
클래스의 변수에 접근하고자 할때 다음과 같이 super
키워드를 작성한다.
SuperCar
클래스
public class SuperCar extends Car {
int gearStage;
int mode;
void manualGearShift(int stage) {// 기어 변속
this.gearStage=stage;
System.out.println("기어 단수: "+ this.gearStage);
}
void driveMode(int mode) {// 운전모드
this.mode=mode;
switch(mode) {
case 1:
System.out.println("운전모드: 스포츠");
break;
case 2:
System.out.println("운전모드: 컴포트");
break;
default:
System.out.println("운전모드: "+ this.mode);
break;
}
}
@Override
void drive(float speed) {// 슈퍼카 이동
super.speed=speed;// Car클래스 변수 참조
System.out.println("슈퍼카 속도: "+super.speed+"km");
}
}
이와 같이 부모클래스의 멤버변수를 참조할때 super
키워드를 사용한다.
super
는 조상의 멤버와 자신의 멤버를 구분한다는 것 외에는 this
키워드와 차이가 없다. 즉, 위에서 본 코드의 경우 this
키워드를 이용해 작성해도 동일한 동작을 한다. 이는 부모 클래스로부터 상속을 받았다면 부모클래스의 멤버도 자식클래스 자신의 멤버이기 때문이다.
조금 더 친숙하게 예를 들면 필자가 부모로부터 돈을 상속받았으면 상속받은 돈이 결국 필자의 돈이라는 개념과 같다. 클래스도 단지 "상속" 받았을 뿐, 결국에는 자식클래스의 멤버가 되기 때문에 this
키워드를 사용해도 동작한다.
SuperCar
클래스
public class SuperCar extends Car {
int gearStage;
int mode;
void manualGearShift(int stage) {// 기어 변속
this.gearStage=stage;
System.out.println("기어 단수: "+ this.gearStage);
}
void driveMode(int mode) {// 운전모드
this.mode=mode;
switch(mode) {
case 1:
System.out.println("운전모드: 스포츠");
break;
case 2:
System.out.println("운전모드: 컴포트");
break;
default:
System.out.println("운전모드: "+ this.mode);
break;
}
}
@Override
void drive(float speed) {// 슈퍼카 이동
this.speed=speed;// this도 사용가능.
System.out.println("슈퍼카 속도: "+this.speed+"km");
}
}
그러나, 위 코드와 같이 자식클래스의 멤버변수와 부모클래스의 멤버변수가 같을 경우에는 super
키워드를 사용해야 부모클래스의 멤버에 접근할 수 있다.
SuperCar
클래스
public class SuperCar extends Car {
int gearStage;
int mode;
float speed;// 변수 이름 중복(부모클래스의 멤버랑 이름이 같음)
void manualGearShift(int stage) {// 기어 변속
this.gearStage=stage;
System.out.println("기어 단수: "+ this.gearStage);
}
void driveMode(int mode) {// 운전모드
this.mode=mode;
switch(mode) {
case 1:
System.out.println("운전모드: 스포츠");
break;
case 2:
System.out.println("운전모드: 컴포트");
break;
default:
System.out.println("운전모드: "+ this.mode);
break;
}
}
@Override
void drive(float speed) {// 슈퍼카 이동
super.speed=speed;// super 사용!!!
System.out.println("슈퍼카 속도: "+super.speed+"km");
}
}
이때는 this
키워드를 사용하면 자식클래스인 SuperCar
클래스의 멤버변수 speed
의 값이 되며, super
키워드를 사용하면 부모클래스인 Car
클래스의 멤버변수 speed
의 값이 돤다.
따라서, 이러한 예외적인 상황이 발생할 수 있기 때문에 부모클래스의 멤버를 참조하고자 하면 this
키워드를 사용할 수 있는 상황이더라도 super
키워드를 사용하는 습관을 갖자.
this()
가 인스턴스 자신의 생성자를 가리키듯super()
는 부모클래스의 생성자를 호출하는데 사용된다.
상속을 받으면 자식클래스는 부모클래스의 인스턴스를 전부 물려받게 된다. 이때, 생성자 역시 물려받게 되기 때문에 부모클래스에 생성자가 있으면 자식클래스 내의 생성자에서 부모클래스의 생성자를 호출해서 부모클래스를 초기화 해야 한다. super
키워드는 이때 사용이 된다.
예를 들어보자.
public class Car {
boolean startupState;
float speed;
{// 초기화 블럭
this.startupState=false;
this.speed=0.0f;
}
Car(){}
Car(float speed){
this.speed=speed;
}
void startUp() {// 시동걸기
this.startupState=true;
System.out.println("시동걸림.");
}
void drive(float speed) {// 이동
this.speed=speed;
System.out.println("속도: "+this.speed+"km");
}
void stop() {// 브레이크
this.speed=0.0f;
this.startupState=false;
System.out.println("시동끔.");
}
void horn() {// 경적
System.out.println("빵빵!! 피자빵!! 초코빵!! 크림빵!!");
}
}
위와 같이 생성자가 있는 클래스를 상속받을때는 부모클래스의 생성자를 자식클래스의 생성자 내에서 호출을 해야 하며, 아래와 같이 이는 super();
키워드를 통해 호출한다.
public class SuperCar extends Car {
int gearStage;
int mode;
SuperCar(){
super(0.0f);// 부모클래스 생성자 호출
}
SuperCar(int mode){
super();// 부모클래스 생성자 호출
this.mode=mode;
}
void manualGearShift(int stage) {// 기어 변속
this.gearStage=stage;
System.out.println("기어 단수: "+ this.gearStage);
}
void driveMode(int mode) {// 운전모드
this.mode=mode;
switch(mode) {
case 1:
System.out.println("운전모드: 스포츠");
break;
case 2:
System.out.println("운전모드: 컴포트");
break;
default:
System.out.println("운전모드: "+ this.mode);
break;
}
}
@Override
void drive(float speed) {// 슈퍼카 이동
super.speed=speed;
System.out.println("슈퍼카 속도: "+this.speed+"km");
}
}
물론, super
키워드가 없을 경우에는 컴파일러에서 자동으로 super();
키워드를 추가하지만, 되도록이면 자식클래스의 생성자 내에서 super키워드를 이용해 부모클래스의 생성자를 호출하는 습관을 갖자.