08.상속
상속이란?
- 부모클래스가 가지는 멤버를 자식클래스가 물려받아 자신의 멤버인 것처럼 사용할 수 있도록 만든 기술이다.
- 다만, 생성자는 상속받지 못하며, 부모가 가지고 있는 private 멤버는 접근할 수 없다.
(protected로 변경하면 상속 관계에 있기 때문에 호출 가능_
- 자바는 하나의 클래스만을 부모클래스로 가지는 단일 상속만 지원한다.
- 클래스 간의 상속 시에는 부모 클래스의 확장의 개념으로 extends 키워드를 사용한다.
ex) public class Academy extends Company{} → Academy 클래스는 Company 클래스를 상속한다.
- 추가적인 자신의 멤버 작성이 가능하며 메소드 재정의(overriding)라는 기술을 이용해서 부모가 가진 메소드를 재정의 하는 것도 가능하다.
상속의 장단점
- 장점
- 새로운 클래스를 작성 시 기존에 작성 된 클래스의 재사용 가능
- 클래스간 계층 관계가 형성되어 다형성 문법의 토대가 됨
- 단점
- 부모클래스의 기능 변경 시 자식클래스의 정상적인 동작 유무 예측이 힘듦
- 반대로 자식클래스가 물려받아 사용하는 기능들을 부모클래스에서 변경 시 어려움이 따름
- 부모클래스로부터 물려받은 기능이 자식클래스에서는 무의미 할 수 있음
→ 장단점을 고려해 볼 때 IS-A 관계로 구분되는 경우에만 사용해야 함
IS-A
“자식클래스는 (하나의)부모 클래스이다.”의 관계로, 부모클래스의 멤버들을 자식클래스가 상속 받음
▼Circle is a Shape. -> Circle 클래스는 하나의 Shape 클래스이다.
super와 super()
- super
부모클래스의 인스턴스 주소를 보관하는 레퍼런스 변수이며, 자식클래스 내의 모든 생성자와 메소드 내에서 부모클래스의 레퍼런스 변수를 사용할 수 있음
- super()
- 부모 생성자를 호출하는 구문으로 매개변수의 타입, 개수, 순서가 일치하는 부모의 생성자를 호출하게 됨(private 생성자를 제외)
- 모든 생성자의 맨 첫 줄에 부모의 기본 생성자를 호출하는 구문인 super(); 를 컴파일러가 자동으로 추가한다.
- 명시적(부모 클래스에 기본 생성자가 없는 경우), 묵시적 모두 가능하다.
(this()는 해당 클래스의 생성자를 호출하는 구문이다.)
오버라이딩(Overriding)
- 정의
부모클래스에서 상속받은 메소드를 자식클래스가 재정의하여 사용하는 것
- 성립조건
- 메소드 이름 동일
- 메소드 리턴 타입 동일
- 매개변수의 타입, 개수, 순서가 동일
- private 메소드는 오버라이딩 불가능
- final 키워드가 사용된 메소드는 오버라이딩 불가능
- 접근제한자는 부모 메소드와 같거나 더 넓은 범위여야 함
- 예외처리는 같은 예외이거나 더 구체적(하위)인 예외를 처리해야 함
- @Override 어노테이션
JDK 1.5부터 추가 된 문법으로 오버라이딩 성립 요건을 체크하여 성립 되지 않은 경우 컴파일 에러를 발생시킨다.
▼ 예시 : 부모 클래스 Product
public class Product {
/* 필드(전역 변수) */
private String code; //상품코드
private String brand; //제조사
private String name; //상품명
private int price; //가격
private Date manufacturingDate; //제조일자
/* 기본 생성자 */
public Product() {
super();
System.out.println("Product 클래스의 기본 생성자 호출");
}
/* 모든 필드를 초기화 하는 생성자 */
public Product(String code, String brand, String name, int price, Date manufacturingDate) {
super();
this.code = code;
this.brand = brand;
this.name = name;
this.price = price;
this.manufacturingDate = manufacturingDate;
}
/* getters, setters 생략 */
/* 모든 필드 값을 문자열로 반환하는 메소드 */
public String getInformation() {
return "Product [code=" + code + ", brand=" + brand + ", name=" + name + ", price="
+ price + ", manufacturingDate=" + manufacturingDate + "]";
}
▼ Product 를 상속받은 Computer 클래스
/* Computer 는 하나의 Product 이다. (IS-A) */
public class Computer extends Product {
/* Computer만 가지는 추가적인 속성을 필드로 정의한다. */
private String cpu;
private int hdd;
private int ram;
private String operationSystem;
/* 기본 생성자 */
public Computer() {
super();
}
/* 모든 필드를 초기화 하는 생성자 */
public Computer(String cpu, int hdd, int ram, String operationSystem) {
/* 부모 클래스의 기본 생성자 호출 */
super();
this.cpu = cpu;
this.hdd = hdd;
this.ram = ram;
this.operationSystem = operationSystem;
}
/* 부모의 필드까지 모두 초기화 하는 생성자 */
public Computer(String code, String brand, String name, int price, Date manufacturingDate,
String cpu, int hdd, int ram, String operationSystem) {
/* 부모 클래스의 모든 필드 초기화 매개변수 생성자 호출 */
super(code, brand, name, price, manufacturingDate);
this.cpu = cpu;
this.hdd = hdd;
this.ram = ram;
this.operationSystem = operationSystem;
}
/* getter, setter 생략*/
/* 부모 필드의 메소드에 대해서는 자신의 멤버처럼 사용 가능하므로 따로 작성할 필요가 없다.
* 자식 클래스에 추가 된 필드에 대해서만 작성한다.*/
/* 모든 필드 값을 문자열로 반환하는 메소드 */
@Override
public String getInformation() {
/* 부모 클래스에 작성한 getter를 이용해서 부모 필드가 가지고 있는 값도 한 번에 문자열 합치기
* 부모가 가진 멤버는 super. 와 this. 둘 다 사용이 가능하다.
* 하지만 코드의 의미를 명확히 하기 위해 super. 을 사용*/
return "Computer ["
+ "code=" + super.getCode()
+ ", brand=" + super.getBrand()
+ ", name=" + super.getName()
+ ", price=" + super.getPrice()
+ ", manufaturingDate=" + super.getManufacturingDate()
+ ", cpu=" + this.cpu
+ ", hdd=" + this.hdd
+ ", ram=" + this.ram
+ ", operationSystem=" + this.operationSystem
+ "]";
/* super.getInformation() : 정상적으로 부모의 메소드를 호출한다.
* this.getInformation() : 재귀 호출(자기 자신의 메소드를 다시 호출하는 것)이 일어나며 StackOverflowError 발생한다.
* getInformation() : this. 이 자동으로 추가 되어 동일하게 재귀 호출이 일어난다. */
return super.getInformation()
+ " Computer ["
+ "cpu=" + this.cpu
+ ", hdd=" + this.hdd
+ ", ram=" + this.ram
+ ", operationSystem=" + this.operationSystem
+ "]";
}