Welcome! Everything is fine.

[3-1] 오버라이딩과 다형성(3) ~ 다형성 활용과 다운캐스팅(4) 본문

자격증 및 기타 활동/J2KB

[3-1] 오버라이딩과 다형성(3) ~ 다형성 활용과 다운캐스팅(4)

개발곰발 2021. 8. 25.
728x90

💡 가상 메서드(virtual method)

가상 메서드란 인스턴스의 메서드가 호출되는 것을 말한다. 재정의가 되어있지 않은 경우 부모 클래스의 메서드를 호출하지만, 재정의가 되어있을 경우 메서드 영역은 2개가 된다. 이때 재정의된 자식 클래스의 메서드를 호출하는 것이다. 가상메서드에 의해서 다형성이 구현된다.

 

아래 코드를 예시로 들면, 부모 클래스인 Customer 클래스에서의 calcPrice()메서드는 자식 클래스인 GoldCustomer 클래스에서 기능이 더 추가되어야한다. 보너스 포인트 비율도 다르고, 할인도 해주기 때문이다. 그래서 GoldCustomer 클래스를 보면, calcPrice()메서드를 재정의하여 사용했다.

public class Customer {
	
	protected int customerID;
	protected String customerName;
	protected String customerGrade;
	int bonusPoint;
	protected double bonusRatio;

	public Customer(int customerID, String customerName) {
		this.customerID = customerID;
		this.customerName = customerName;
		customerGrade = "SILVER";
		bonusRatio = 0.01;
	}
	
	public int calcPrice(int price) {
		
		if(customerGrade == "SILVER") {
			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;
	}
}

아래 GoldCustomer 클래스에선 기존의 Customer 클래스와 비교했을 때 bonusRatio의 값도 다르고, saleRatio라는 값도 새로 생긴 것을 볼 수 있다.

public class GoldCustomer extends Customer{
	
	double saleRatio;
	
	public GoldCustomer(int customerID, String customerName) {
		super(customerID, customerName);
		customerGrade = "Gold";
		bonusRatio = 0.02;
		saleRatio = 0.1;
	}

	@Override
	public int calcPrice(int price) {
		bonusPoint += price * bonusRatio;
		return price - (int)(price * saleRatio);
	}
}

아래 코드에서 customer.을 찍었을 때 Customer 클래스의 변수와 메서드가 뜨더라도, calcPrice() 메서드는 GoldCustomer 클래스에서 재정의한 내용을 따른다.

import java.util.ArrayList;

public class CustomerTest {

	public static void main(String[] args) {
		ArrayList<Customer> customerList = new ArrayList<Customer>();
        
		Customer customerLee = new Customer(10010, "이순신");
		Customer customerShin = new Customer(10011, "신사임당");
		Customer customerHong = new GoldCustomer(10012, "홍길동");
		Customer customerYul = new GoldCustomer(10013, "이율곡");
		VIPcustomer customerKim = new VIPcustomer(10014, "김유신", 12345);
		
		customerList.add(customerLee);
		customerList.add(customerShin);
		customerList.add(customerHong);
		customerList.add(customerYul);
		customerList.add(customerKim);
		
		System.out.println("========= 고객정보 출력 =========");
		for(Customer customer : customerList) {
			System.out.println(customer.showCustomerInfo());
		}
		System.out.println("========= 할인율과 보너스 포인트 결과 =========");
		int price = 10000;
		for(Customer customer : customerList) {
			int cost = customer.calcPrice(price);
			System.out.println(customer.getCustomerName() + "님이" + cost + 
            "원을 지불하셨습니다.");
			System.out.println(customer.showCustomerInfo());
		}	
	}
}

객체를 관리하는데 가장 좋은 클래스인 ArrayList를 사용하였으며, 출력 결과는 다음과 같다.

========= 고객정보 출력 =========
이순신 님의 등급은 SILVER이며, 보너스 포인트는 0입니다.
신사임당 님의 등급은 SILVER이며, 보너스 포인트는 0입니다.
홍길동 님의 등급은 Gold이며, 보너스 포인트는 0입니다.
이율곡 님의 등급은 Gold이며, 보너스 포인트는 0입니다.
김유신 님의 등급은 VIP이며, 보너스 포인트는 0입니다.담당 상담원의 ID는12345입니다.
========= 할인율과 보너스 포인트 결과 =========
이순신님이10000원을 지불하셨습니다.
이순신 님의 등급은 SILVER이며, 보너스 포인트는 100입니다.
신사임당님이10000원을 지불하셨습니다.
신사임당 님의 등급은 SILVER이며, 보너스 포인트는 100입니다.
홍길동님이9000원을 지불하셨습니다.
홍길동 님의 등급은 Gold이며, 보너스 포인트는 200입니다.
이율곡님이9000원을 지불하셨습니다.
이율곡 님의 등급은 Gold이며, 보너스 포인트는 200입니다.
김유신님이9000원을 지불하셨습니다.
김유신 님의 등급은 VIP이며, 보너스 포인트는 500입니다.담당 상담원의 ID는12345입니다.

💡 다형성(polymorphism)

다형성이란 하나의 코드가 여러가지 자료형으로 구현되어 실행되는 것을 말한다. 객체 지향 프로그래밍의 유연성, 재활용성, 유지보수성에 기본이 되는 특징이다. 다형성은 하나의 클래스를 상속받은 여러 클래스가 있는 경우 각 클래스에 같은 이름의 서로 다른 메서드를 재정의함으로써 구현될 수 있다. 아래 코드에서 다형성을 잘 설명해준다. Animal 클래스를 상속받아 Human, Tiger, Eagle 4개의 클래스를 만들었고, move() 메서드를 각각 그에 맞게 재정의했다.

class Animal{
	public void move() {
		System.out.println("동물이 움직인다.");
	}
}

class Human extends Animal{
	public void move() {
		System.out.println("사람이 두발로 걷는다.");
	}
}

class Tiger extends Animal{
	public void move() {
		System.out.println("호랑이가 네발로 뛴다.");
	}
}

class Eagle extends Animal{
	public void move() {
		System.out.println("독수리가 하늘을 난다.");
	}
}

public class AnimalTest {

	public static void main(String[] args) {
		
		AnimalTest test = new AnimalTest();
		test.moveAnimal(new Human());
		test.moveAnimal(new Tiger());
		test.moveAnimal(new Eagle());
	}
	
	public void moveAnimal(Animal animal) {
		animal.move();
	}
}

출력 결과는 다음과 같다. 동일한 animal.move() 코드에 대해 각기 다르게 구현되는 것을 볼 수 있다.

사람이 두발로 걷는다.
호랑이가 네발로 뛴다.
독수리가 하늘을 난다.

📃 상속은 언제 사용할까?

IS-A 관계 : 일반적인 개념과 구체적인 개념과의 관계. 단순히 코드를 재사용하는 목적으로 사용되지는 않는다.

  • 상위 클래스 : 일반적인 개념 클래스
  • 하위 클래스 : 구체적인 개념 클래스

HAS-A 관계 : 한 클래스가 다른 클래스를 소유한 관계. 코드 재사용의 한 방법이다.

💡 다운 캐스팅 - instanceof

하위 클래스가 상위 클래스로 형 변환 되는 것은 묵시적으로 이루어진다. 다시 원래 자료 형인 하위 클래스로 형 변환하려면 명시적으로 다운캐스팅을 해야한다. 상속을 받았다고 해서 상위 클래스의 메서드만 쓴다거나 오버라이딩을 하는 것이 아니라, 해당 클래스만의 메서드가 들어갈 수 있다. 그러나 다운캐스팅보다는 상속관계에서 해결할 수 있다면 오버라이딩을 쓰는 것이 좋다. 다운 캐스팅을  할 때는 instanceof 라는 키워드를 사용하고, 타입을 체크하는 역할을 한다.

 

아래 코드를 보면 Human, Tiger, Eagle 클래스에 각기 다른 새로운 메서드가 생겼다. 따라서 instanceof 키워드로 타입체크를 한 뒤 형 Animal 클래스의 하위 클래스로 형 변환을 해주었다.

class Animal{
	public void move() {
		System.out.println("동물이 움직인다.");
	}
}

class Human extends Animal{
	public void move() {
		System.out.println("사람이 두발로 걷는다.");
	}
	
	public void readbook() {
		System.out.println("사람이 책을 읽는다.");
	}
}

class Tiger extends Animal{
	public void move() {
		System.out.println("호랑이가 네발로 뛴다.");
	}
	
	public void hunting() {
		System.out.println("호랑이가 사냥을 한다.");
	}
}

public class AnimalTest {

	public static void main(String[] args) {
		
		AnimalTest test = new AnimalTest();
		test.moveAnimal(new Human());
		test.moveAnimal(new Tiger());
		test.moveAnimal(new Eagle());
	}
	
	public void moveAnimal(Animal animal) {
		animal.move();
        
		if(animal instanceof Human) {
			Human h = (Human)animal;
			h.readbook();
		}
		
		else if(animal instanceof Tiger) {
			Tiger t = (Tiger)animal;
			t.hunting();
		}
		
		else if(animal instanceof Eagle) {
			Eagle e = (Eagle)animal;
			e.flying();
		}
		
		else {
			System.out.println("지원되지 않는 기능입니다.");
		}
	}  
}

출력 결과는 다음과 같다.

사람이 두발로 걷는다.
사람이 책을 읽는다.
호랑이가 네발로 뛴다.
호랑이가 사냥을 한다.
독수리가 하늘을 난다.
날개를 퍼덕거린다.

만약 instanceof 키워드를 사용하지 않고 아래와 같이 쓴다면 오류가 난다.

// 오류발생
Human h = (Human)animal;
h.reading();
📃 3주차 시작, ArrayList 사용법을 자꾸 까먹어서 다시 정리하고 익혀봐야겠다.