[java] 상속 & 오버라이딩

상속(inheritance)

- 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.

- 상속을 통해서 클래스를 작성하면 보다 적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 있기 때문에 코드의 추가 및 변경이 매우 용이하다. => 코드의 재사용성을 높이고, 코드의 중복을 제거하며, 프로그램의 생산성과 유지보스에 기여한다.

ex) class Child(자손클래스) extends parent(조상클래스) { ... }

- 조상 클래스가 변경되면 자손 클래스는 자동적으로 영향을 받게 되지만, 자손 클래스가 변경되는 것은 조상 클래스에 아무런 영향을 주지 못한다.

- 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다. 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

- 클래스 간의 관계에서 형제 관계과 같은 것은 없다. 부모와 자식의 관계(상속관계)만이 존재할 뿐이다.

- 같은 내용의 코드를 하나 이상의 클래스에 중복적으로 추가해야하는 경우에는 상속관계를 이용해서 코드의 중복을 최소화해야한다.

- 자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버도 함께 생성되기 때문에 따로 조상 클래스의 인스턴스를 생성하지 않고도 조상 클래스의 멤버들을 사용할 수 있다.(조상 클래스의 멤버와 자손 클래스의 멤버가 합쳐진 하나의 인스턴스로 생성된다.)

 

클래스간의 관계 - 포함관계

- 한 클래스의 멤버변수로 다른 클래스를 선언하는 것을 뜻한다.

- 하나의 거대한 클래스를 작성하는 것보다 단위별로 여러 개의 클래스를 작성한 다음, 이 단위 클래스들을 포함관계로 재사용하면 보다 간결하고 손쉽게 클래스를 작성할 수 있다.

ex)---------------------------------

class Car{

Engine e = new Engine();

Door d = new Door();

}-----------------------------------

 

클래스간의 관계 결정하기(is-a / has-a)

ex)---------------------------------------

원은 점이다. Circle is a Point.

원은 점을 가지고 있다. Circle has a Point.

------------------------------------------

- '~은 ~이다.'라는 문장이 성립한다면, 서로 상속관계를 맺어 주고, '~은 ~을 가지고 있다.'는 문장이 성립한다면 포함관계를 맺어 준다.

 

단일상속(single inheritance)

- 자바에서는 단일상속만을 허용하며 포함상속을 함으로 다중상속을 대신한다. 다중상속을 허용하면 여러 클래스로부터 상속받을 수 있기 
때문에 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다는 장점이 있지만, 클래스간의 관계가 매우 복잡해진다는것과 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다는 단점을 가지고 있다.(static메서드라면 클래스의 이름을 붙여서 구별할 수 있지만, 인스턴스메서드의 경우 선언부가 같은 두 메서드를 구별할 수 있는 방법은 없다.)

 

Object클래스 - 모든 클래스의 조상

- 모든 클래스 상속계층도의 제일 위에 위치하는 조상클래스이다. 다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 한다.

ex)-----------------------------------------------------------

class TV { ... } ====(컴파일)====> class TV extends Object { ... }

--------------------------------------------------------------

- 다른 클래스로부터 상속을 받는다고 하더라도 상속계층도를 따라 올라가다 보면 결국 마지막 최상위 조상은 Object클래스일 것이다.

 

 

오버라이딩(overriding)

- 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것을 오버라이팅이라고 한다. 상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스 자신에 맞게 변경해야하는 경우가 많다. 이때 조상의 메서드를 오버라이딩한다.

 

오버라이딩의 조건

(자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와)

- 이름이 같아야 한다.

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

- 리턴타입이 같아야 한다.

=> 즉, 선언부가 서로 일치해야한다. 다만 접근 제어자와 예외는 제한된 조건 하에서만 다르게 변경할 수 있다.

└ 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경 할 수 없다.(만일 조상 클래스에 정의된 메서드의 접근 제어자가 protected라면, 이를 오버라이딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이어야 한다.)

└ 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.(주의! Exception은 모든 예외의 최고 조상이므로 가장 많은 개수의 예외를 던질 수 있다)

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

 

오버로딩 vs 오버라이딩

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

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

 

super

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

- 조상 클래스로부터 상속받은 멤버도 자손 클래스 자신의 멤버이므로 super대신 this를 사용할 수 있다.(참고! 조상클래스 참조변수 = new 자손클래스();  //이와같은 promotion도 같은 원리) 그래도 멤버가 중복 정의되어 서로 구별해야하는 경우에만 super를 사용하는 것이 좋다.

- 모든 인스턴스메서드에는 자신이 속한 인스턴스의 주소가 지역변수로 저장되는데, 이것이 참조변수인 this와 super의 값이 된다.

ex)----------------------

class Car{

Car(Car this) { ... }

}------------------------

- static메서드는 인스턴스와 관련이 없다. 그래서 this와 마찬가지로 super 역시 static메서드에서는 사용할 수 없고 인스턴스메서드에서만 사용할 수 있다.

- 메서드 역시 super를 써서 호출할 수 있다. 특히 조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에 super를 사용한다.

 

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

- 조상 클래스의 생성자를 호출하는데 사용된다.

- 자손 클래스의 인스턴스를 생성하면, 자손의 멤버와 조상의 멤버가 모두 합쳐진 하나의 인스턴스가 생성된다. 그래서 자손 클래스의 인스턴스가 조상 클래스의 멤버들을 사용할 수 있는 것이다. 이 때, 조상 클래스의 멤버의 생성과 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다. 생성자의 첫 줄에서 조상클래스의 생성자를 호출해야하는 이유는 자손 클래스의 멤버가 조상 클래스의 멤버를 사용할 수도 있으므로 조상의 멤버들이 먼저 초기화되어 있어야 하기 때문이다. 따라서 최고 조상인 Object클래스를 제외한 모든 클래스의 생성자는 첫 줄에 반드시 자신의 다른 생성자 또는 조상의 생성자를 호출해야한다. 그렇지 않으면 컴파일러는 생성자의 첫 줄에 'super();'를 자동적으로 추가할 것이다.

- 조상 클래스의 멤버변수는 조상의 생성자에 의해 초기화되도록 해야 하는 것이다.

(주의! 사용자가 선언한 생성자가 있을 경우 디폴트 생성자는 자동으로 생성되지 않는다.)