오버라이딩이란?
o 조상 클래스로부터 상속받은 내용을 변경하는 것을 오버라이딩이라고 한다. 상속받은 메소드를 자손 클래스 자신에 맞게 변경해야 하는 경우 오버라이딩을 한다.
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;
}
}
|
- Point 클래스는 2차원 좌표였으나 Point3D는 3차원 좌표이기에 z축 멤버 변수가 추가되었다. 따라서 getLocation()메소드 호출 시 z축에 대한 값도 출력을 위해 오버라이딩 하였다.
오버라이딩 조건
o 오버라이딩은 메서드의 내용만 새로 작성하는 것이기에 메서드의 선언부는 조상의 메서드와 완전히 일치해야한다.
o 오버라이딩 성립하기 위한 조건
- 자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와 1. 이름이 같아야 한다. 2. 매개변수가 같아야 한다. 3. 반환타입이 같아야 한다. |
o 요약하면 선언부가 완전히 일치해야 한다. 다만, 접근 제어자와 예외는 제한된 조건에서 다르게 변경할 수 있다.
1. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
조상클래스에 정의된 메서드의 접근 제어자가 protected라면, 이를 오버라이딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이어야 하며 대부분의 경우 같은 접근 제어자를 사용한다.
※접근 제어자의 접근범위는 넓은 것에서 좁은 것 순으로 public, protected, (default), private이다.
2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
조상클래스의 메서드에 예외 개수가 2개이면 자손 클래스의 오버라이딩 메소드의 예외 개수는 2개보다 많게 선언할 수 없다.
class Parent{
void parentMethod() throws IOException, SQLException{
}
}
class Child extends Parent{
void parentMethod() throws Exception{ //에러
}
}
|
위처럼 오버라이딩을 했을 경우 예외의 개수로는 조상클래스보다 적다. 그러나 Exception은 모든 예외의 최고 조상이므로 더 많은 예외 개수를 던질 수 있도록 선언한 것이기에 잘못된 오버라이딩이다.
3. 인스턴스 메서드를 static메서드 또는 그 반대로 변경할 수 없다.
오버로딩 vs 오버라이딩
o 오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는 것 (new) <- 같은 메소드 이름 다른 역할(매개 변수)
오버라이딩(overriding) : 상속받은 메서드의 내용을 변경하는 것 (change, modify)
class Parent{
void parentMethod() { }
}
class Child extends Parent{
void parentMethod() { } //오버 로딩
void parentMethod(int i) { } //오버 라이딩
void childMethod() { }
void childMethod(int i) { } // 오버 라이딩
}
|
super
o super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다. 멤버 변수와 지역 변수의 이름이 같은 경우 this를 붙여 구별하듯이 상속받은 멤버와 자신의 멤버가 이름이 같은 때는 super를 붙여서 구별할 수 있다.
o 조상 클래스로부터 상속받은 멤버도 자손 클래스 자신의 멤버이기에 super대신 this로 사용할 수도 있기에 조상 클래스 멤버와 자손 클래스 멤버가 중복 정의되어 서로 구별이 필요한 경우에만 super를 사용하는 것이 좋다.
o 모든 인스턴스는 자신이 속한 인스턴스 주소가 지역 변수로 저장되는데 이것이 참조변수인 this와 super이다.
o static 메서드는 인스턴스와 관련이 없기에 this와 마찬가지로 super또한 인스턴스 메서드에서만 사용할 수 있다.
public class Test {
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); // 조상 클래스
}
}
|
o 변수뿐만 아니라 메서드에서도 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를 사용해서 조상클래스의 메서드를 포함시키는 것이 좋다. 이렇게 하면 나중에 조상 클래스의 메서드 내용이 변경되어도 자동으로 반영되기 때문이다.
super( ) – 조상 클래스의 생성자
o this( )가 같은 클래스의 생성자를 호출하는 데 사용했다면 super( )는 조상클래스의 생성자를 호출하는데 사용된다.
o 자손 클래스의 인스턴스를 생성 시 조상 클래스의 멤버도 포함되어 있기에 조상 클래스 멤버에 대한 초기화 작업이 필요하다. 그래서 자손 클래스 생성자에서 조상 클래스 생성자가 호출되어야 한다.
o 조상클래스의 생성자 호출은 자손 클래스 생성자 맨 첫 줄에 호출되어야 하는데 자손 클래스 멤버가 조상 클래스 멤버를 사용할 수도 있기에 먼저 초기화가 되어있어야 하기 때문이다.
o Object클래스를 제외한 모든 클래스는 생성자의 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야 한다. 그렇지 않으면 컴파일러가 자동으로 super( );를 생성자 첫 줄에 추가한다.
class Point{
int x;
int y;
// 조상 클래스 생성자
Point(int x, int y){
// super(); // 컴파일 시 자동으로 추가
this.x = x;
this.y = y;
}
String getLocation() {
return "x : " + x + "y : " + y;
}
}
class Point3D extends Point{
int z;
// 자손 클래스 생성자
Point3D(){ // 기본 생성자
this(100, 200, 300); // Point3D(int x, int y, int z) 호출
// 에러 발생X, 첫 줄에 super가 아닌 생성자 this사용
}
Point3D(int x, int y, int z) {
// super(); //에러, 조상 클래스에서 Point()생성자를 찾을 수 없음
super(x,y); //조상 클래스 생성자 형식에 맞게 호출
this.z = z;
}
/*
Point3D(int x, int y, int z) {
// 에러, 첫줄에 super();가 없다
this.x = x;
this.y = y;
this.z = z;
// super(); // 에러, 첫줄이 아니기에 에러 발생
}
*/
String getLocation() { // 오버라이딩
return super.getLocation() + "z : " + z;
}
}
|
- Point3D 생성자 첫 줄에 다른 생성자를 작성하지 않으면 컴파일 시 super()[=Point()]를 자동으로 첫 줄에 넣어준다. 그러나 조상 클래스인 Point 클래스에 기본 생성자인 Point()가 없어 컴파일 에러가 발생한다.
(생성자가 정의되어 있는 클래스에는 컴파일러가 자동으로 기본 생성자를 추가하지 않는다.)
- 따라서 조상 클래스(Point)에서 기본 생성자Point()를 수동으로 추가하거나 자손 클래스의 Point3D 생성자에서 조상 클래스 생성자 형식에 맞게 추가하면 된다.
- Point 클래스의 생성자는 어떤 생성자도 호출하고 있지 않기에 컴파일 시 첫 줄에 super()가 추가되어 조상 클래스인 Object클래스를 호출하게 된다.
o 인스턴스 생성 시 생성자 호출 순서는 아래와 같다.
Point3D p3 = new Point3D(); ->Point3D( ) -> Point3D(int x, int y, int z) -> Point(int x, int y) -> Object() |
|
1. 인스턴스 생성 2. 기본 생성자 호출 3. (오버 로딩)생성자 호출 4. 조상 클래스 생성자 호출 5. 조상 클래스 생성자 호출 |
Point3D p3 = new Point3D(); Point3D( ) Point3D(int x, int y, int z) Point(int x, int y) Object( ) |
'언어 > 자바의 정석' 카테고리의 다른 글
[자바의 정석] Chapter 7 객체 지향 프로그래밍II (4) - 제어자(modifier) (0) | 2021.02.16 |
---|---|
[자바의 정석] Chapter 7 객체 지향 프로그래밍II (3) - package와 import (0) | 2021.02.16 |
[자바의 정석] Chapter 7 객체 지향 프로그래밍II(1) - 상속 (0) | 2021.02.16 |
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(6) - 변수의 초기화 (0) | 2021.02.04 |
[자바의 정석] Chapter 6 객체 지향 프로그래밍 요약(5) - 생성자 (0) | 2021.02.04 |