JAVA/객체 지향

상속(extends) (1)

츄빙 2024. 4. 24. 00:43

단일 상속

-자바는 단일 상속만을 지원하므로 extends 키워드 뒤에는 단 하나의 클래스만 올 수 있음

 

언제 상속을 구현하는가?

1) A 클래스가 있고, 이 클래스는 일반적인 개념을 가진 클래스이다.

2) A 클래스 보다는 기능이 좀 더 많고, 구체적이다.

=> A 클래스를 상속받는 B 클래스를 생성

 

※ 주의

1) 기존에 어떤 괜찮은 클래스가 있다.

2) 그 기능을 가져다 쓰고 싶다.

=> 이럴때 상속을 쓰는게 아님(일반적 -> 구체적 관계가 성사되지 않음)


상속을 활용한 멤버십 클래스 구현하기

멤버십 시나리오

1. 회사에서 고객 정보를 활용한 맞춤 서비스를 하기 위해 일반고객과,
    이보다 충성도가 높은 우수고객에 따른 서비스를 제공하고자 함.
2. 물품을 구매 할 때 적용되는 할인율과 적립되는 보너스 포인트의 비율이 다름.
3. 여러 멤버십에 대한 각각 다양한 서비스를 제공할 수 있음.

 


멤버십에 대한 구현을 클래스 상속을 활용하여 구현해보기

 

일반 고객 클래스 구현

  • 고객의 속성: 고객 아이디, 고객 이름, 고객 등급, 보너스 포인트, 보너스 포인트 적립 비율
  • 일반 고객의 경우 물품 구매시 1%의 보너스 포인트 적립

Customer.java

package ch02;

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer() {
		customerGrade = "SILVER";
		bonusRatio = 0.01;
        
        System.out.println("Customer() call");
	}
	
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price;
	}
	
	public String showCustomerInfo() {
		return customerName + "님의 등급은 " + customerGrade + "이며, 보너스 포인트는 " + bonusPoint + "입니다";
	}

	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;
	}
}

 

 

우수 고객 구현

  • 매출에 더 많은 거래를 하는 단골 고객
  • 제품을 살 때 10%를 할인해 줌
  • 보너스 포인트는 제품 가격의 5%를 적립해 줌
  • 담당 전문 상담원이 배정됨

VIPCustomer.java

package ch02;

public class VIPCustomer extends Customer{
	private String agentID;
	double salesRatio;
	
	public VIPCustomer(){
		//super();
		bonusRatio = 0.05;
		salesRatio = 0.1;
		customerGrade = "VIP";
        
        System.out.println("VIPCustomer() call");
	}

	public String getAgentID(){
		return agentID;
	}

	public void setAgentID(String agentID){
		this.agentID = agentID;
	}
	
}

 

 

상속 테스트

 

CustomerTest.java

package ch02;

public class CustomerTest {
	public static void main(String[] args) {		
		VIPCustomer c2 = new VIPCustomer();
		c2.setCustomerID(2);
		c2.setCustomerName("추상옥");
		c2.bonusPoint = 10000;
		System.out.println(c2.showCustomerInfo());
	}
}

 

실행 결과 화면

 

 

VIPCustomer(자식 클래스)가 생기기 전에 Customer(부모 클래스) 생성자가 먼저 호출 되는 것을 확인할 수 있다.

 

how?

컴파일러가 하위 클래스 생성자에서 상위 클래스를 호출하는 코드가 없다면, super() 키워드를 자동적으로 넣어줌(VIPCustomer 생성자의 주석처리 부분)

 -> Customer() (default 생성자) 가 호출 됨

 

default 생성자를 없애고 싶다면?

상위 클래스의 기본 생성자가 없는 경우 (다른 생성자가 있는 경우) 하위 클래스의 생성자에서는 super를 이용하여 명시적으로 상위 클래스의 생성자를 호출함(수동)

//수정된 Customer.java

package ch02;

public class Customer {
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	double bonusRatio;
	
	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
    
    //Getter
    //Setter

 

 이제, super()를 불러올 수 없게되어, VIPCustomer.java에서 에러가 날 것이다.

그럼 VIPCustomer 생성자도 바꿔주자

 

//수정된 VIPCustomer.java

package ch02;

public class VIPCustomer extends Customer {
	private String agentID;
	double salesRatio;
	
	public VIPCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		
		customerGrade = "VIP";
		bonusRatio = 0.05;
		salesRatio = 0.1;
	}

 

//수정된 CustomerTest.java

package ch02;

public class CustomerTest {
	public static void main(String[] args) {
		VIPCustomer c2 = new VIPCustomer(2, "추상옥");
		c2.bonusPoint = 10000;
		System.out.println(c2.showCustomerInfo());
	}
}

 


 

형 변환(업캐스팅): 상위 클래스로 변수를 선언하고 하위 클래스의 생성자로 인스턴스를 생성

Customer c1 = new VIPCustomer(3, "noname");

c1.agentID = 100; // error
c1.salesRatio = 0.5 //error
c1.bonusPoint = 3000;
c1.bonusRatio = 0.01;

 

위 코드에서 VIPCustomer() 생성자에 의해 VIPCustomer 클래스의 모든 멤버 변수에 대한 메모리는 생성되었지만, 변수의 타입이 Customer 이므로 실제 접근 가능한 변수나 메서드는 Customer의 변수와 메서드임!!

 -> c1은 Customer 클래스의 멤버 변수(bonusPoint, bonusRatio)에 접근 가능하지만 VIPCustomer 클래스의 멤버 변수(agentID, salesRatio)에는 접근 불가능)  

 


상위 클래스 타입의 변수에 하위 클래스 변수 대입 가능

int addCustomer(Customer customer) {

}

VIPCustomer vCustomer = new VIPCustomer();
addCustomer(vCustomer);

 

하위 클래스는 상위 클래스의 타입을 내포하고 있으므로 상위 클래스로의 묵시적 형 변환이 가능