객체 지향 프로그래밍의 중요한 특징 중 하나가 상속(inheritance)이다.
상속은 우리가 일반적으로 알 듯 무엇인가 물려받는다는 의미이다.
B 클래스가 A 클래스를 상속받으면 B 클래스는 A 클래스의 멤버 변수와 메서드를 사용할 수 있다.
객체 지향 프로그램은 유지보수하기 편하고 프로그램을 수정하거나 새로운 내용을 추가하는 것이 유연한데, 그 기반이 되는 기술이 바로 상속이다.
클래스의 상속
B 클래스가 A 클래스에서 상속받는다고 할 때 다음과 같은 그림으로 나타낼 수 있다.

우리가 생각하기에 상속을 하는 클래스에서 상속을 받는 클래스로 화살표가 갈 것 같지만, 클래스 간 상속을 표현할 때는 위 그림에서 표현한 것처럼 상속받는 클래스에서 상속하는 클래스로 화살표가 가므로 헷갈리면 안된다.
앞으로 부모 클래스를 '상위 클래스', 자식 클래스를 '하위 클래스' 라고 부르겠다.
클래스 상속 문법
자바 문법으로 상속을 구현할 때는 extends 예약어를 사용한다.
이때 사용하는 extends예약어는 '연장, 확장하다'의 의미이다.
즉 A 가 가지고 있는 속성이나 기능을 추가로 확장하여 B 클래스를 구현한다는 뜻이다.
그러면 일반적인 클래스 A에서 더 구체적인 클래스 B가 구현된다.
아래 코드는 'B 클래스가 A 클래스를 상속받는다' 라고 한다.
class B extends 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 + "입니다.";
}
}
- 사용한 멤버 변수
| 멤버 변수 | 설명 |
| customerID | 고객 아이디 |
| customerName | 고객 이름 |
| customerGrade | 고객 등급 - 기본 생성자에서 지정되는 기본 등급은 SILVER이다. |
| bonusPoint | 고객의 보너스 포인트 - 고객이 제품을 구매할 경우 누적되는 보너스 포인트이다. |
| bonusRatio | 보너스 포인트 적립 비율 - 고객이 제품을 구매할 때 구매 금액의 일정 비율이 보너스 포인트로 적립된다. 이때 계산되는 적립 비율이다. - 기본 생성자에서 지정되는 적립 비율은 1%이다. 즉 만원짜리를 사면 백원 적립된다. |
- 사용한 메서드
| 메서드 | 설명 |
| Customer() | 기본 생성자이다. 고객 한 명이 새로 생성되면 customerGrade는 SILVER이고, bonusRatio는 1%로 지정한다. |
| calcPrice(int price) | 제품에 대해 지불해야 하는 금액을 계산하여 반환한다. 할인되지 않은 경우 가격을 그대로 반환한다. 그리고 가격에 대해 보너스 포인트 비율을 적용하여 보너스 포인트를 적립한다. |
| showCustomerInfo() | 고객 정보를 출력한다. 고객 이름과 등급, 현재 적립된 포인트를 보여 준다. |
새로운 고객 등급이 필요한 경우
- 예제 시나리오
고객이 점점 늘어나고 판매도 많아지다 보니 단골이 생겼다.
단골은 회사 매출에 많은 기여를 하는 우수 고객이다.
이 우수 고객에게 좋은 혜택을 주고 싶다.
우수 고객 등급은 VIP이고, 다음과 같은 혜택을 제공한다.
- 제품을 살 때는 항상 10% 할인
- 보너스 포인트를 5% 적립
- 담당 전문 상담원을 배정
이 요구 사항을 어떻게 구현하면 좋을까?
간단히는 Customer 클래스가 존재하니 Customer 클래스에 VIP 고객에게 필요한 변수와 메서드까지 함께 포함하여 구현하는 것이다.
그런데 이렇게 구현하면 Customer 클래스의 코드가 복잡해진다.
게다가 일반 고객의 인스턴스를 생성할 때는 VIP 고객과 관련된 기능은 전혀 필요 없는데 VIP 고객의 내용까지 같이 생성되어 낭비가 발생한다.
이런 경우는 클래스를 따로 만드는게 좋다.
package inheritance;
public class VIPCustomer {
private int customerID;
private String cusotmerName;
private String customerGrade;
int bonusPoint;
double bonusRatio;
private int agentID;
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 cusotmerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다.";
}
}
만들고 보니 앞에 만든 Customer 클래스와 겹치는 멤버 변수와 메서드가 보인다.
게다가 calcPrice() 메서드는 이름은 같은데 구현 내용은 다르다.
생각해 보면 VIP 고객도 어쨌든 고객이다.
다만 일반 고객에게 제공하는 혜택을 기본으로 제공하고 추가 속성과 메서드가 있는 것이다.
바로 이런 경우에 상속을 사용한다.
즉 Customer 클래스에 일반 고객의 속성과 기능이 이미 구현되어 있기 때문에, VIPCustomer 클래스는 Customer 클래스를 상속받고 VIP 고객에게 필요한 추가 속성과 기능을 구현하는 것이다.
Customer 클래스를 상속한 VIPCustomer 클래스의 코드는 다음과 같다.
package inheritance;
public class VIPCustomer extends Customer{
private int agentID;
double saleRatio;
public VIPCustomer(){
customerGrade = "VIP"; // 상위 클래스에서 private 변수이므로 오류 발생
bonusRatio = 0.05;
saleRatio = 0.1;
}
public int getAgentID(){
return agentID;
}
}
VIPCustomer 클래스의 코드가 상당히 간단해졌다.
Customer 클래스에 이미 선언되어 있는 멤버변수, 메서드는 상속 받아서 사용할 것이기 때문에 구현하지 않았다.
그런데 위 코드에는 두가지 문제가 있다.
첫번째, customerGrade 변수에서 오류가 발생한다.
두번째, VIP 고객에게 제공하는 혜택인 할인율과 세일 가격을 어떻게 적용할지 구현하지 않았다는 점이다.
두번째 내용은 나중에 메서드 오버라이딩에서 다룬다.
상위 클래스 변수를 사용하기 위한 protected 예약어
우선 첫번째 문제부터 해결하겠다.
이 오류는 상위 클래스에 선언한 customerGrade가 private 변수이기 때문이다.
상위 클래스에 작성한 변수나 메서드 중 외부 클래스에서 사용할 수 없지만 하위 클래스에서는 사용할 수 있도록 지정하는 예약어가 바로 protected이다.
상속받은 하위 클래스에서는 public처럼 사용할 수 있는 것이다.
즉 protected는 상속된 하위 클래스를 제외한 나머지 외부 클래스에서는 private과 동일하다.
Cusotmer 클래스에 있는 private 변수를 다른 하위 클래스에서도 사용할 수 있도록 모두 protected로 바꾼다.
그리고 protected로 선언한 customerID, customerName,customerGrade 변수를 사용하기 위해 get(), set() 메서드를 추가한다.
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 + "입니다.";
}
}
protected 예약어로 선언한 변수는 외부 클래스에는 private 변수처럼 get() 메서드를 사용해 값을 가져올 수 있고,
set() 메서드를 사용해 값을 지정할 수 있다.
Customer 클래스를 상속받은 VIPCustomer 클래스는 protected로 선언한 변수를 상속받게 되고, 나머지 public 메서드도 상속받아 사용할 수 있다.
테스트 프로그램 실행하기
그러면 간단한 테스트 프로그램을 만들어 두 클래스를 생성해 본다.
- 예제 시나리오
일반 고객 1명과 VIP 고객 1명이 있다.
일반 고객의 이름은 이순신, 아이디는 10010이다.
이 고객은 지금 보너스 포인트 1000점이 있다.
VIP 고객의 이름은 김순신, 아이디는 10020이다.
이 고객은 보너스 포인트 10000점을 가지고 있다.
package inheritance;
public class CustomerTest1 {
public static void main(String[] args){
Customer customerLee = new Customer();
customerLee.setCustomerID(10010);
customerLee.setCustomerName("이순신");
customerLee.bonusPoint = 1000;
System.out.println(customerLee.showCustomerInfo());
VIPCustomer customerKim = new VIPCustomer();
customerKim.setCustomerID(10020);
customerKim.setCustomerName("김순신");
customerKim.bonusPoint = 10000;
System.out.println(customerKim.showCustomerInfo());
}
}
5행에서 새로운 Customer 클래스를 생성하고 customerLee 참조 변수에 대입한다.
11행에서는 VIPCustomer 클래스를 생성하고 customerKim 참조 변수에 대입한다.
VIPCustomer가 Customer를 상속했기 때문에 고객 아이디, 고객 이름의 메서드를 사용할 수 있다.
두 고객의 속성인 아이디, 이름, 보너스 포인트를 지정하고 showCustomerInfo() 메서드를 사용해 고객 정보를 출력했다.
'JAVA' 카테고리의 다른 글
| 메서드 오버라이딩, 가상 메서드 (1) | 2024.12.20 |
|---|---|
| 상속에서 클래스 생성과 형 변환, super() (0) | 2024.12.19 |
| ArrayList 사용한 배열 응용 프로그램 (2) | 2024.12.19 |
| ArrayList (0) | 2024.12.19 |
| 다차원 배열 (0) | 2024.12.18 |