728x90
상속과 다형성을 배워야 하는 이유
- 객체 지향 프로그램에서 지원하는 여러 기술을 활용하면 재사용성, 확장성이 좋고 유지보수가 수월한 프로그램을 구현할 수 있습니다.
- 상속과 다형성이 이에 해당합니다.
- 또한 객체 지향 프로그램의 다형성을 활용하면 유연한 구조이 프로그램을 구현할 수 있습니다.
- 매우 중요한 내용이므로 필수적으로 학습하시길 바랍니다.
상속이란?
- 객체 지향 프로그래밍의 중요한 특징 중 하나가 상속(inheritance)입니다.
- 상속은 우리가 일반적으로 알 듯 무엇인가를 물려받는다는 의미입니다.
- 자식이 부모로부터 무엇인가를 물려받으면 자식은 그것을 사용할 수 있습니다.
- 객체 지향 프로그램에서도 마찬가지입니다. B 클래스가 A 클래스를 상속받으면 B 클래스는 A 클래스의 멤버 변수와 메서드를 사용할 수 있습니다.
- 객체 지향 프로그램은 유지 보수하기 편하고 프로그램을 수정하거나 새로운 내용을 추가하는 것이 유연한데, 그 기반이 되는 기술이 바로 상속입니다.
클래스의 상속
- 상속과 관련하여 사용하는 용어와 문법에 대해 먼저 알아보겠습니다.
- 우리가 생각하기에 상속을 하는 클래스에서 상속을 받는 클래스로 화살표가 갈 것 같지만, 클래스 간 상속을 표현할 때는 위 그림에 표현한 것처럼 상속받는 클래스에서 상속하는 클래스로 화살표가 갑니다.
- 부모 클래스 parent class 는 '상위 클래스', 자식 클래스 child class 는 '하위 클래스' 라고 부릅니다.
- 예를 들어볼까요. 포유류는 사람, 개 보다 상위의 일반적인 개념입니다.
- 즉 사람은 포유류의 특징과 기능을 기본으로 더 많거나 다른 특징과 기능을 가지고 있습니다.
- 이렇게 상속 관계에서는 상위 클래스가 하위 클래스보다 일반적인 개념이고, 하위 클래스는 상위 클래스보다 구체적인 클래스가 됩니다.
클래스 상속 문법
- 자바 문법으로 상속을 구현할 때는 extends 예약어를 사용합니다.
- 즉 B extends A 라고 한다면 A 가 가지고 있는 속성이나 기능을 추가로 확장하여 B 클래스를 구현한다는 의미입니다.
- 다른 말로 B 가 A 를 상속받는다고 얘기할 수 있습니다.
- 위 그림 예시를 보면 이런 관계를 확인해볼 수 있습니다.
- 부모 클래스는 상위 클래스로 하위 클래스보다 일반적인 개념이고, 하위 클래스는 상위 클래스보다 구체적인 클래스인 것을 확인할 수 있습니다.
상속을 사용하여 고객 관리 프로그램 구현하기
- 간단한 프로그램을 만들면서 상속을 학습해 보겠습니다.
- 회사에서 고객 맞춤 서비스를 제공하기 위해 고객 관리 프로그램을 구현하려고 합니다.
- 우선 고객 클래스가 있어야 합니다.
- 이 예제에서는 고객 아이디, 이름, 고객 등급, 보너스 포인트, 보너스 포인트 적립 비율 속성으로 선언하겠습니다.
package inheritance;
public class Customer {
private int customerID;
private String customerName;
private String customerGrade;
int bonusPoint;
double bonusRatio;
public Customer() {
customerGrade = "SILVER";
bonusRatio = 0.01;
}
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;
return price;
}
public String showCustomerInfo() {
return (customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.");
}
}
- 우선 Customer 클래스의 멤버 변수와 멤버 메서드를 구현했습니다.
- 여기까지는 기존의 객체 지향 프로그램과 별반 차이가 없습니다.
- 이제 다음과 같은 상황을 생각해 보겠습니다.
- 새로운 고객 등급이 필요한 경우 (예제 시나리오)
많은 구매를 지속적으로 해주는 단골 고객이 생김에 따라 우수 고객으로 선정하여 VIP 등급을 부여하고 다음과 같은 혜택을 제공할 예정입니다.
- 제품을 살 때는 항상 10% 할인해 줍니다.
- 보너스 포인트를 5% 적립해 줍니다.
- 담당 전문 상담원을 배정해 줍니다. - 이 요구 사항을 어떻게 구현하면 좋을까요?
- 가장 간단한 방법은 이미 구현한 Customer 클래스에 VIP 고객을 구현하는데 필요한 변수와 메서드를 함께 구현하는 겁니다. 그런데, 이렇게 구현하기 시작하면 Customer 클래스의 코드가 복잡해집니다. 게다가 VIP 가 아닌 고객에게는 해당 코드들이 필요가 없기 때문에 메모리 낭비도 발생합니다. 결국 이런 경우에는 VIPCustomer 클래스를 따로 만드는 것이 좋습니다.
package inheritance;
public class VIPCustomer {
private int customerID;
private String customerName;
private String customerGrade;
int bonusPoint;
double bonusRatio;
private int agentID; // VIP 고객 관련 기능을 구현할 때만 필요한 멤버 변수
double saleRatio;
public VIPCustomer() {
customerGrade = "VIP";
bonusRatio = 0.05;
saleRatio = 0.1;
}
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;
return price - (int)(price * saleRatio); // 할인율 적용
}
public int getAgentID() {
return agentID;
}
public String showCustomerInfo() {
return customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
}
}
- 위와 같이 클래스를 구현했습니다.
- 그런데, 클래스를 만들고 보니 앞에서 만든 Customer 클래스와 겹치는 멤버 변수와 메서드가 보입니다.
- 게다가 calcPrice( ) 메서드는 이름은 같은데 구현 내용은 다릅니다.
- 또한 VIP 고객도 큰 범주에서는 고객에 속합니다. 다만 일반 고객에 없는 추가 속성과 메서드가 있는 것입니다.
- 바로 이런 경우에 상속을 사용합니다.
- 즉 Customer 클래스에 일반 고객의 속성과 기능이 이미 구현되어 있기 때문에, VIPCustomer 클래스는 Customer 클래스를 상속받고 VIP 고객에게 필요한 추가 속성과 기능을 구현하는 것입니다.
- Customer 클래스를 상속한 VIPCustomer 클래스의 코드는 다음과 같습니다.
package inheritance;
public class VIPCustomerExtends extends Customer{
private int agentID;
double saleRatio;
public VIPCustomerExtends() {
customerGrade = "VIP"; // 상위 클래스에서 private 변수이므로 오류 발생
bonusRatio = 0.05;
saleRatio = 0.1;
}
public int getAgentID() {
return agentID;
}
}
- VIPCustomer 클래스의 코드가 간단해졌습니다
- Customer 클래스에 이미 선언되어 있는 customerID, customerName, customerGrade, bonusPoint, bonusRatio 멤버 변수와 calcPrice( ), showCustomerInfo( ) 메서드는 상속을 받아서 사용할 것이기 때문에 구현하지 않아도 됩니다.
- 그런데, 위의 코드에는 2가지 문제가 있습니다.
- 첫 번째, customerGrade 변수는 상위 클래스에서 private 으로 접근 제어자를 설정한 변수입니다. 따라서 외부 클래스에서는 이 변수를 사용할 수 없습니다.
- 두 번째, VIP 고객에게 제공하는 혜택인 할인율과 세일 가격을 어떻게 적용할지 구현하지 않았습니다. (오버라이딩 파트에서 보완할 예정)
상위 클래스 변수를 사용하기 위한 protected 예약어
- 우선 customerGrade 변수에서 발생하는 오류부터 수정해 보겠습니다.
- 이 오류는 상위 클래스에 선언한 customerGrade 가 private 변수이기 때문에 발생합니다.
- 상위 클래스에서 작성한 변수나 메서드 중 외부 클래스에서 사용할 수 없지만 하위 클래스에서는 사용할 수 있도록 지정하는 예약어가 바로 protected 입니다.
- 상속받은 하위 클래스에서는 public 처럼 사용할 수 있습니다. 즉 상속된 하위 클래스를 제외한 나머지 욉 ㅜ클래스에서는 private 과 동일한 역할을 합니다.
package inheritance;
public class Customer {
protected int customerID;
protected String customerName;
protected String customerGrade;
int bonusPoint;
double bonusRatio;
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;
}
public Customer() {
customerGrade = "SILVER";
bonusRatio = 0.01;
}
public int calcPrice(int price) {
bonusPoint += price * bonusRatio;
return price;
}
public String showCustomerInfo() {
return (customerName + " 님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.");
}
}
- 위와 같이 Customer 클래스에 있는 private 변수를 다른 하위 클래스에서도 사용할 수 있도록 모두 protected 로 바꾸었습니다.
- 또한 해당 변수를 외부 클래스에서도 사용할 수 있도록 Getter 와 Setter 를 마련해 주었습니다.
- 위와 같이 protected 로 선언하면 VIPCustomer 에 있던 오류는 사라지게 됩니다.