[java] 11. 오버라이딩


오버라이딩



전 장의 예제에서 봤듯이 부모 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 합니다.

상속받은 메서드를 그대로 사용하기도 하지만, 자식 클래스 자신에 맞게 변경해야하는 경우가 많습니다.

이럴 때 부모의 메서드를 오버라이딩합니다

예를 들어 2차원 좌표계의 한 점을 표현하기 위한 Point 클래스가 있을 때,

이를 부모로 하는 Point3D클래스를 정의한다고 합시다.


//Point 클래스

class Point{
	int x;
	int y;
	
	String getLocation(){
		return "x :"+x+", y:"+y;
	}
}

//Point3D 클래스
class Point3D extends Point{
	int z;
	
	String getLocation(){	//오버라이딩
		return "x:"+x+", y:"+y+", z:"+z;
	}
}



Point 클래스를 사용하던 사람들은 새로 작성된 Point3D 클래스가 Point클래스의 자식이므로

Point3D클래스의 인스턴스에 대해서 getLocation()을 호출하면 Point클래스의 getLocation()이 그랬듯이

점의 좌표를 문자열로 얻을 수 있을 것이라고 기대할 것입니다.

그렇기 때문에 Point3D 클래스의 문자열을 반환하는 메서드를 새로 정의하기보다는

부모클래스의 메서드와 이름은 같지만 다른 기능을 하게끔 오버라이딩하는 것이 좋습니다.



오버라이딩의 조건



오버라이딩은 메서드의 내용만을 새로 작성하는 것이므로 메서드의 선언부는 부모의 것과 완전히 일치해야합니다.

그래서 오버라이딩이 성립하기 위해서는 다음과 같은 조건을 만족해야 합니다.


자식클래스에서 오버라이딩하는 메서드는 부모클래스의 메서드와

1. 이름이 같아야 한다.

2. 매개변수가 같아야 한다.

3. 반환타입이 같아야 한다.



접근 제어자와 예외는 제한된 조건 하에서만 다르게 변경할 수 있습니다.


부모 클래스의 메서드를 자식 클래스에서 오버라이딩할 때,

1.접근 제어자를 부모 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

ex) 부모클래스의 접근 제어자가 protected라면, 자식 클래스는 protected나 public이어야 한다.


2.예외는 부모 클래스의 메서드보다 많이 선언할 수 없다.


3.인스턴스메서드를 static메서드로 또는 그 반대로 변경할 수 없다.



접근제어자와 예외는 후에 배웁니다!


부모클래스에 정의된 static 메서드를 자식 클래스에서 똑같은 이름의 static 메서드로 정의할 수도 있습니다.


하지만, 이것은 각 클래스에 별개의 static 메서드를 정의한 것일뿐 오버라이딩이 아닙니다.



오버로딩 vs 오버라이딩



오버로딩과 오버라이딩은 서로 혼동하기 쉽지만 사실 그 차이는 명백합니다.

오버로딩은 기존에 없는 새로운 메서드를 추가하는 것이고,

오버라이딩은 부모로부터 상속받은 메서드의 내용을 변경하는 것입니다.


오버로딩 : 매개변수를 달리하여 기존에 없는 새로운 메서드를 정의하는 것

오버라이딩 : 상속받은 메서드의 내용을 변경하는 것



super



super는 자식 클래스에서 부모 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수입니다.

멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이

상속받은 멤버와 자신의 클래스에 정의된 멤버의 이름이 같을 때는 super를 붙여서 구별할 수 있습니다.

또한, 부모클래스의 메서드를 호출할 때도 super를 이용해서 호출할 수 있습니다.

특히, 부모클래스의 메서드를 자식 클래스에서 오버라이딩한 경우에 super를 사용합니다.

다음은 super를 사용한 예제입니다.


image



그리고 오버라이딩한 메서드 위에는 다음과 같이 문장을 써줍니다.


@Override //추가
String method(){ //오버라이딩한 메서드
	...
}



위의 문장은 컴파일러에게 오버라이딩한 메서드라는 것을 알리는 것입니다.



super() - 부모 클래스의 생성자



this()와 마찬가지로 super() 역시 생성자입니다.

this()는 같은 클래스의 다른 생성자를 호출하는데 사용되지만,

super()는 부모 클래스의 생성자를 호출하는데 사용됩니다.

항상 자식 클래스의 생성자 맨 첫번째 줄에서는 부모 클래스의 생성자를 호출해줘야합니다.

그 이유는 자식 클래스의 멤버가 부모 클래스의 멤버를 사용할 수도 있으므로

부모의 멤버들이 먼저 초기화되어 있어야 하기 때문입니다.

부모 클래스 생성자의 호출은 클래스의 상속관계를 거슬러 올라가면서 계속 반복됩니다.

마지막으로 모든 클래스의 최고 조상인 Object 클래스의 생성자인 Object()까지 가서야 끝이납니다.

그래서 Object 클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 부모의 생성자를 호출해야 합니다.


Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super()를 호출해야 한다.

그렇지 않으면 컴파일러가 자동적으로 super();를 생성자의 첫 줄에 삽입한다.



다음 예제를 보시죠


image



위의 코드는 컴파일 오류를 발생시킵니다.

그 이유는 Point3D의 생성자 첫 줄에 자신의 생성자 또는 부모의 생성자를 호출하지 않았기 때문에

컴파일러가 자동적으로 Point3D의 생성자 첫줄에 super();을 삽입합니다.

하지만 Point3D의 부모인 Point에는 이미 생성자 Point(int x,int y)가 정의되었기 때문에

디폴트 생성자인 Point()가 정의되어있지 않습니다.

따라서 오류가 발생하였습니다.

따라서 Point3D 생성자 맨 첫 줄에 super(x,y); 를 추가해주면 오류가 고쳐집니다.


image



그리고 Point3D p3=new Point3D(); 와 같이 인스턴스를 생성하면,

아래와 같은 순서로 생성자가 호출됩니다.


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



하지만 Point3D의 생성자가 호출 되자마자 Point의 생성자가 호출되므로

Point3D의 생성자 코드들을 다 실행 후 Point의 생성자 코드를 실행하는 것이 아니라

Point의 생성자가 먼저 실행 된 후 Point3D의 생성자 코드들이 실행됩니다.


image