오버라이딩이란?
조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것이다. 상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스에 맞게 변경해야하는 경우 오버라이딩한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Point {
int x;
int y;
String getLocation() {
return "x :" + x + ", y :" + y;
}
}
class Point3D extends Point {
int z;
String getLocation() { // 오버라이딩
return "x :" + x + ", y :" + y + ", z :" + z;
}
}
오버라이딩의 조건
자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와 선언부가 같아야 한다.
- 이름이 같아야 한다.
- 매개변수가 같아야 한다.
- 반환타입이 같아야 한다.
다만 접근 제어자와 예외는 제한된 조건 하에서만 다르게 변경할 수 있다.
접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다. ex) 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 이를 오버라이딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이어야 한다.
넓은 것에서 좁은 것 순으로 나열 : public, protected, (default), private
조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class Parent { void parentMethod() throws IOException, SQLException { // ... } } class Child extends Parent { void parentMethod() throws IOException { // 예외의 개수가 적으므로 가능 // ... } } class Child2 extends Parent { void parentMethod() throws Exception { // 예외의 개수가 적지만 Exception은 모든 예외의 최고 조상이므로 불가능 // ... } }
인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.
static메서드를 똑같은 이름의 static메서드로 정의할 수 있지만 이것은 각 클래스에 별개의 static메서드를 정의한 것일 뿐 오버라이딩이 아니다.
오버로딩 vs. 오버라이딩
오버로딩(overloading) 기존에 없는 새로운 메서드를 정의하는 것(new)
오버라이딩(overriding) 상속받은 메서드의 내용을 변경하는 것(change, modify)
1
2
3
4
5
6
7
8
9
10
11
12
class Parent {
void parentMethod() {}
}
class Child extends Parent {
void parentMethod() {} // 오버라이딩
void parentMethod(int i) {} // 오버로딩
void childMethod() {}
void childMethod(int i) {} // 오버로딩
void childMethod() {} // 에러. 중복정의 되었음
}
super
자손 클래스에서 조상 클래스로부터 상속받은 맴버를 참조하는데 사용되는 참조변수이다. 맴버변수와 지역변수의 이름이 같을 때 this를 사용해서 구별했듯이 상속받은 맴버와 자신의 클래스에 정의된 맴버의 이름이 같을 때는 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
class SuperTest {
public static void main(String[] args) {
Child c = new Child();
c.method();
}
}
class Parent {
int x = 10;
}
class Child extends Parent {
void method() {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("super.x = " + super.x);
}
}
/*
[실행결과]
x = 10
this.x = 10
super.x = 10
*/
이 경우 x, this.x, super.x 모두 같은 변수를 의미한다.
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
class SuperTest2 {
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);
System.out.println("this.x = " + this.x);
System.out.println("super.x = " + super.x);
}
}
/*
[실행결과]
x = 20
this.x = 20
super.x = 10
*/
같은 이름의 맴버변수가 조상 클래스에 있고 자손 클래스에도 있을 때는 super.x와 this.x는 서로 다른 값을 참조하게 된다.
super() - 조상 클래스의 생성자
this()와 마찬가지로 super() 역시 생성자이다. this()는 같은 클래스의 다른 생성자를 호출하는데 사용되지만, super()는 조상 클래스의 생성자를 호출하는데 사용된다.
Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자, this() 또는 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
class PointTest {
public static void main(String[] args) {
Point3D p3 = 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();'를 여기에 삽입한다.
// super()는 Point3D의 조상인 Point클래스의 기본 생성자인 Point()를 의미한다.
// 하지만 조상 클래스의 Point()가 없기 때문에 컴파일에러가 발생할 것이다.
this.x = x;
this.y = y;
this.z = z;
}
String getLocation() { // 오버라이딩
return "x : " + x + ", y : " + y + ", z : " + z;
}
}
아래처럼 변경하면 문제없이 컴파일 될 것이다.
1
2
3
4
5
Point3D(int x, int y, int z) {
super(x, y); // 조상클래스의 생성자 Point(int x, int y)를 호출한다.
this.z = z;
}
출처
자바의 정석