본문 바로가기

프로그래밍/JAVA

[JAVA] 오버라이딩(overriding)

JAVA의 정석(2nd Editionm) (남궁 성 著) 238p~246p 를 참조해 코드를 작성했으며

개인적인 공부 내용을 적은 것이므로 오류가 있을 수 있습니다.


0. 들어가기에 앞서


오버라이딩은 상속(inheritance)와 관련이 깊은 내용이다. 따라서 공부하기 이전에 상속에 대한 내용을 충분히 숙지하기 바란다.  (링크  : http://whatisthenext.tistory.com/19)


공부하면서 객체지향 프로그래밍은 "효율화"를 지향한다고 느꼈다. 

효율화에 관점에서 본다면, 왜 상속이 필요한지, 왜 오버라이딩이 필요한지 조금이나마 알 수 있을 것 같다.


1. 오버라이딩(overriding)


조상 클래스로부터 상속받은 메서드(method) 내용을 자식 클래스에서 변경(변수 또는 메서드)하는 것.

over + writing과 발음이 비슷하니, "기존 메서드를 이용해 다시 쓰기"라고 생각하면 더 쉽다.


class Point {
int x;
int y;

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

class Point3D extends Point {
int z;

String getLocation() { // getLocation()을 상속받음.

return "x : " + x + ", y : " + y + ", z :" + z; }
}


위 예제에서


조상클래스는 class Point이다. 

조상클래스의 메서드는 getLocation(){ return x, y, z}이다.

조상클래스로부터 상속받은 메서드 역시 getLocastion(){ 내용생략 }이다.


자손클래스는 class Point3D이다.

자손클래스가 상속받은 메서드는 getLocation() { 내용생략 } 이다.

자손클래스가 상속받은 메서드에 + z 를 추가해줌으로써 "오버라이딩"해주고 있다.




2 오버라이딩의 성립조건


위 예제를 통해 다시 살펴보자


이름, 반환타입, 매개변수가 모두 같다는 걸 알 수 있다. 이를 선언부가 일치한다고 한다.

(다른 말로는 메서드 시그니처(signiture, 서명) 이라고도 한다.)


단, 자손 클래스에서 오버라이딩할 때 조건이 있다.


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

※ public → protected  (default)  private순으로 접근범위가 좁아진다. ※

  - 조상 클래스에 정의된 메서드 접근 제어자가 protected라면 

    자손 클래스에 정의된 메서드 접근 제어자는 protected나 public이어야 한다.


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

 - 조상 클래스에서 thorws IOExcpetion, SQLException 예외 2개를 예외한다면

   자손 클래스에서 thorws IOException 또는 SQLException 중 1개만 예외할 수 있다.

               ※ 단, 예외의 최고조상 Exception 은 가장 많은 개수의 예외이므로 주의해야 한다.


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




3. 오버로딩 vs 오버라이딩


오버로딩(overloading) : 기존에 없는 새로운 메서드를 정의하는

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




4. super


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

(즉, 슈퍼클래스를 참조)


※ this : 멤버변수와 지역변수의 이름이 같을 때 사용하는 참조변수. (즉, 서브클래스를 참조)


super를 이용해 조상 클래스로부터 상속받은 멤버를 참조


super를 이용해 조상 클래스의 메서드를 

자손 클래스에서 오버라이딩(상속받은 메서드의 내용 변경)하는 경우 사용




5. super() - 조상 클래스의 생성자



생성자의 의미에 대해 다시 한 번 짚고 넘어가겠다.

생성자는 "인스턴스"가 생성될 때 호출되는 '인스턴스 초기화 메서드' 이다.

생성자는 "클래스" 내에 선언되며, 리턴값이 없다!


super() : 조상 클래스의 생성자를 호출하는데 사용

※ this() : 같은 클래스의 다른 생성자를 호출하는 데 사용


조상클래스의 생성자를 호출해야 하는 이유는, 자손클래스의 인스턴스를 생성하면

자손의 멤버 + 조상의 멤버가 합쳐진 하나의 인스턴스가 탄생된다. 

따라서, 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 한다.



자손클래스 Point3D의 생성자 첫 줄이 생성자를 호출하는 문장이 아니기 때문에

컴파일러 Point3D의 생성자 첫 줄에 super(); 자동으로 넣어준다.


그래서 코드 맨 위로 다시 올라가면 Point3D p3 = new Point3D(1,2,3,);으로

인스턴스를 생성하면, 생성자 Point3D(int x, int y, int z)가 호출되면서


위에서 넣어준 첫 줄 super();를 수행하게 된다. 

흐름을 따라가면 super(); → (조상) Point클래스의 기본 생성자 Point()를 호출한다.

그러나 Point클래스에서 생성자 Point()를 정의하지 않았다. 따라서 컴파일 에러가 발생한다.



따라서 자손클래스 Point3D 클래스 생성자 첫줄에서 조상클래스의 생성자 Point(int x, int y)를 호출하도록

super(x, y); 를 추가하면 된다.


즉, 조상클래스의 멤버변수는 super을 이용해서 조상의 생성자에 의해 초기화 되도록 해야 한다.