상속(inheritance)
상속
클래스에서 상속의 의미는 새로운 클래스를 정의할 때 이미 구현된 클래스를 상속(inheritance)받아서 속성이나
기능이 확장되는 클래스를 구현하는 것이다.
class B extends A { }
B클래스는 A클래스를 상속받아 사용하겠다는 것이다.
여기서 A클래스가 상위클래스가 되고 B클래스가 하위클래스가 된다.
객체지향에서 코드의 재사용이라고 보기는 어렵다. 재사용과는 다르다.
상위클래스는 하위클래스보다 일반적인 개념과 기능을 갖고
하위클래스는 상위클래스보다 구체적인 개념과 기능을 갖는다.
상속에 대해 배울때마다 많이 본 예시가 포유류 - 사람 의 관계이다.
사람은 포유류에 속하지만 모든 포유류는 사람이 아니다.
사람은 포유류 중 하나의 개체가 되는것이고 사람만의 특징이 있다.
상속의 개념으로 보자면 사람은 포유류의 공통적인 개념과 기능을 상속받아 그 개념과 기능을 갖고 있으며
사람만의 특징을 추가적으로 갖게 된다.
그럼 사람이라는 하위클래스는 포유류의 공통적인 개념과 기능을 갖고 있으면서 하위클래스에서는 사람만의
특징을 추가적으로 더 갖게 되는것이다.
자바에서는 single inheritance만을 지원하기 때문에 하위클래스에서 extends 뒤에는 하나의 class만 사용할 수 있다.
상속 구현 예제
고객의 등급에 따른 차별화된 서비스를 제공하기 위함이다.
각 기능은 따로 구현하지 않고
Customer class
public class Customer {
protected int customerId;
protected String customerName;
protected String customerGrade;
//Constructor
public Customer(int customerId, String customerName){
this.customerId = customerId;
this.customerName = customerName;
customerGrade = "Silver";
}
//getter setter
public int getCustomerId() {
return customerId;
}
public void setCustomerId(int customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerGrade() {
return customerGrade;
}
public void setCustomerGrade(String customerGrade) {
this.customerGrade = customerGrade;
}
@Override
public String toString() {
return "고객번호 " + customerId + " " + customerName + "님의 등급은 "
+ customerGrade + "이며, 적립된 보너스 포인트는 " + bonusPoint + "점 입니다.";
}
}
GoldCustomer Class
public class GoldCustomer extends Customer{
public GoldCustomer(int customerId, String customerName){
super(customerId, customerName);
customerGrade("Gold");
}
}
VIPCustomer Class
public class VIPCustomer extends Customer{
public VIPCustomer(int customerId, String customerName) {
super(customerId, customerName);
customerGrade("VIP");
}
}
CustomerTest Class
public class CustomerTest {
public static void main(String[] args) {
Customer customerHong = new Customer(10010, "홍길동");
customerHong.bonusPoint = 1000;
System.out.println(customerHong.toString());
GoldCustomer customerLee = new GoldCustomer(10020, "이순신");
customerLee.bonusPoint = 5000;
System.out.println(customerLee.toString());
VIPCustomer customerKim = new VIPCustomer(10030, "김유신");
customerKim.bonusPoint = 10000;
System.out.println(customerKim.toString());
}
}
홍길동님의 등급은 Silver이며, 적립된 보너스 포인트는 1000점 입니다.
이순신님의 등급은 Gold이며, 적립된 보너스 포인트는 5000점 입니다.
김유신님의 등급은 VIP이며, 적립된 보너스 포인트는 10000점 입니다.
이렇게 출력된다.
각 등급클래스들은 Customer를 상속받아 Id와 Name, bonusPoint를 처리하고 Grade를 재정의한다.
그리고 그 정보들을 toString을 통해 출력하게 된다.
Customer 클래스에서 각 변수들은 protected로 처리함으로써 상속받은 클래스들은 접근할 수 있지만
상속받지 않은 외부클래스에서는 접근할 수 없다.
상속에서 클래스 생성과정과 형변환
하위클래스가 생성될 때 상위클래스가 먼저 생성된다.
상위클래스의 생성자가 호출 된 다음 하위클래스의 생성자가 호출되며
하위클래스의 생성자에서는 무조건 상위클래스의 생성자가 호출되어야 한다.
하위 클래스에서 상위클래스의 생성자를 호출하는 코드가 없는 경우 컴파일러는 상위클래스의 기본 생성자를
호출하기 위한 super()를 실행한다.
하지만 상위클래스에 기본생성자가 없는 경우(매개변수가 있는 생성자만 존재하는 경우)에는 하위클래스는
명시적으로 상위클래스의 생성자를 호출해야 한다.
//Customer Class
public class Customer {
protected int customerId;
protected String customerName;
protected String customerGrade;
//default Constructor
public Customer() {
customerGrade = "Silver";
}
//getter setter 구현
//toString 구현
}
//VIPCustomer Class
public class VIPCustomer extends Customer {
public VIPCustomer() {
//컴파일러가 super();을 추가해 컴파일 수행
customerGrade = "VIP";
}
}
이와 같은 상황에서 Customer에는 기본생성자가 존재하고 VIPCustomer에서는 Customer를 따로 호출하도록 하지
않았다면 컴파일러가 super()를 추가해 실행함으로써 알아서 Customer의 기본생성자가 호출되는 것이다.
매개변수가 있는 생성자만 존재하고 기본생성자가 존재하지 않는다면 명시적으로 호출해야 한다고 했는데
상속 예제에 구현한게 명시적으로 호출한 경우이다.
//Customer Class
public class Customer {
protected int customerId;
protected String customerName;
protected String customerGrade;
//Constructor
public Customer(int customerId, String customerName) {
this.customerId = customerId;
this.customerName = customerName;
customerGrade = "Silver";
}
//getter setter
//toString
}
//VIPCustomer Class
public class VIPCustomer extends Customer {
public VIPCustomer(int customerId, String customerName) {
super(customerId, customerName);
customerGrade = "VIP";
}
}
위와 같이 Customer 클래스에는 기본생성자가 없이 매개변수가 들어있는 생성자만이 존재한다.
그럼 VIPCustomer 클래스에서 명시하지 않는다면 컴파일러는 super()로 Customer의 기본생성자를 찾을것이고
기본생성자가 존재하지 않기 때문에 오류가 발생한다.
그렇기 때문에 super(customerId, customerName) 이렇게 매개변수를 넣어줌으로써 이 두 매개변수를 갖고 있는
생성자를 호출하라고 명시적으로 호출해야 한다.
매개변수를 넣지 않는다면 현재 Customer는 int와 String을 받고 있으니
super(0, "a"); 이렇게 호출할 수도 있다.
상위클래스로의 묵시적 형 변환(업캐스팅)
상위클래스형으로 변수를 선언하고 하위클래스 인스턴스를 생성할 수 있다.
하위클래스는 상위클래스의 타입을 내포하고 있으므로 상위클래스로 묵시적 형변환이 가능하다.
상속관계에서 모든 하위 클래스는 상위클래스로 묵시적 형변환이 되지만
상위클래스는 하위클래스로 묵시적 형변환이 성립하지 않는다.
Customer vc = new VIPCustomer();
이렇게 사용할 수 있으며 상속받아 쓴다는 것은 상황에 따라 다르겠지만 Customer에 있는 것들을 사용할 수 있다는
것인데 VIPCustomer에서 확장해 사용한다는 의미이다.
그렇기 때문에 Customer에서는 VIPCustomer처럼 사용할 수 없는 것이다.
예제를 예로 들자면 Customer에는 id, name, grade가 존재한다. 그리고 VIPCustomer에서 할인율인 salesRatio가
추가되어서 할인율을 처리하도록 확장했다고 한다면
VIPCustomer는 상속을 받는 클래스이기 때문에 Customer의 모든것을 사용할 수 있지만 Customer는 상속을
해주는 입장이지 상속을 받는 클래스가 아니기 때문에 할인율에 대한 처리를 혼자서 할 수 없는 것이다.
단, Customer vc = new VIPCustomer() 이렇게 업캐스팅한다면 vc로 접근할 수 있는 범위는 Customer로 한정된다.
즉, vc. 으로 사용할 수 있는 것은 Customer에 있는 id, name, grade만 가능하고 VIPCustomer에 추가한
salesRatio는 사용할 수 없다.
방법이 아예 없는것은 아니다. ((VIPCustomer) vc).salesRatio 이렇게 사용할 수 있지만 이렇게 사용할거면
굳이 Customer로 업캐스팅해서 사용할 필요가 없기 때문에 불필요하다고 볼 수 있다.
물론 상황에 따라 다르겠지만..