조상 클래스로부터 상속받은 메서드 내용을 변경하는 것.
상속 받은 메서드와 이름이 같은 메서드를 자손 클래스에서 정의하면 덮어씌워짐(override).
조상 클래스의 메서드를 자손 클래스에서 오버라이딩하기 위해서는 선언부가 동일해야함. (메서드 이름, 매개변수, 반환타입)
다만, 접근 제어자와 예외는 변경 가능한데 다음의 제약조건이 따름
접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경 불가.
대부분 같은 범위의 제어자를 사용함.
접근 제어자의 범위 (내림차순)
public
→protected
→ (default) →private
조상 클래스의 메서드보다 많은 수(혹은 더 넓은 범위)의 예외 선언 불가.
class Parent {
void parentMethod() throws IOException, SQLException {
...
}
}
class Child extends Parent {
void parentMethod() throws Exception {
... // (X) 예외의 개수가 2개보다 많지 않으니 괜찮은 것 같지만, 범위로 봤을 때는 Exception이 모든 예외를 의미하는 것이므로 틀렸음
}
...
}
인스턴스 메서드 ↔ static
메서드 변경 불가.
class Parent {
void parentMethod() { ... }
}
class Child extends Parent {
void parentMethod() { ... } // 오버라이딩
void parentMethod(int i) { ... } // 오버로딩
}
super
는 조상 클래스로부터 상속 받은 멤버(변수, 메서드)를 참조할 때 자손 클래스에서 사용하는 참조변수임
멤버변수와 지역변수가 같은 이름일 때 구분을 위해 this
를 사용하는 것처럼 상속 받은 멤버와 고유한 멤버의 이름이 같을 때 구분을 위해 super
사용.
this
와 super
모두 인스턴스 메서드에 기본적으로 내장된 지역변수로, 소속된 인스턴스의 주소 값이 저장되어 있음.
인스턴스 메서드 안에서만 사용 가능하므로
static
메서드에서는super
사용 불가.
class SuperTest {
public static void main(String[] args) {
Child c = new Child();
c.method();
}
}
class Parent {
int x = 10;
}
class Child extends Parent {
int x = 20;
void method() {
System.out.println(x); // 자손 클래스(현 클래스)의 멤버변수 x의 값 20
System.out.println(this.x); // 자손 클래스(현 클래스)의 멤버변수 x의 값 20
System.out.println(super.x); // 조상 클래스가 상속해준 멤버변수 x의 값: 10
}
}
같은 클래스 내의 다른 생성자를 호출할 때 this()
를 쓰듯이, 조상 클래스의 생성자를 호출할 때는 super()
를 사용함.
인스턴스를 생성하면, 생성자들의 호출 연쇄작용으로 조상 클래스의 생성자들이 순서대로 호출되고, 결국엔 최고 조상인 Object
클래스까지 올라감.
최고 조상인 Object
클래스를 제외한 모든 클래스의 생성자에서는 첫째 줄에 반드시
this()
를 사용하여 같은 클래스 내의 다른 생성자를 호출하거나,
super()
를 사용하여 조상 클래스의 생성자를 호출해주어야 함. (조상 클래스 멤버 초기화)
자손 클래스 멤버들이 조상 클래스 멤버들을 사용할 수도 있기 때문.
만약 작성하지 않으면 컴파일러가 자동으로 추가함. (이 때, 조상 클래스 안에 기본 생성자는 없고 다른 생성자만 있는 경우, 컴파일 에러 발생)
class Point {
int x, y;
// 생성자가 없으므로 컴파일러가 기본 생성자 Point() {} 를 만들어줌.
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
// 이 자리에 컴파일러가 super(); 를 채워줌.
this.x = x;
this.y = y;
this.z = z;
}
}
위 예시에서는 컴파일러가 Point3D
클래스의 생성자 첫 줄에 super();
를 채워주고, Point
클래스에 만들어준 기본 생성자를 참조하게 되므로 정상 작동함.
class Point {
int x, y;
Point(int x, y) {
this.x = x;
this.y = y;
}
// 생성자가 존재하므로 컴파일러가 기본 생성자 Point() {} 를 만들어 주지 않음
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
// 이 자리에 컴파일러가 super(); 를 채워줌.
this.x = x;
this.y = y;
this.z = z;
}
}
그러나 이렇게 Point
클래스에 기본 생성자가 아닌 다른 생성자가 존재함으로써 컴파일러가 기본 생성자를 자동적으로 만들어주지 않는 환경이 되면, Point3D
클래스의 생성자 첫 줄에 생겨나는 super();
에서 참조할 생성자가 없어 컴파일 에러가 발생함.
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
// 이 자리에 컴파일러가 super(); 를 채워줌.
this.x = x;
this.y = y;
this.z = z;
}
}
에러를 방지하려면
Point3D
생성자 첫 줄에 알맞은 매개변수가 담긴super(x, y);
를 적어주거나,Point
클래스에서 기본 생성자를 따로 정의해주면 됨.