다형성이란?
하나의 코드가 여러 자료형으로 구현되어 실행되는 것을 말한다.
쉽게 말해 같은 코드에서 여러 실행 결과가 나오는 것이다.

그림과 같이 3개의 클래스가 Animal 클래스를 상속받는 경우를 생각해보자.
- Animal 클래스에 메서드를 하나 정의하고 상속받은 클래스에서 재정의한다.
package polymorphism;
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 AnimalTest1{
public static void main(String[] args){
AnimalTest1 aTest = new AnimalTest1();
aTest.moveAnimal(new Human());
aTest.moveAnimal(new Tiger());
aTest.moveAnimal(new Eagle());
}
public void moveAnimal(Animal animal){ //매개 변수의 자료형이 상위 클래스
animal.move(); //재정의된 메서드가 호출됨
}
}

테스트를 하기 위해 AnimalTest1 클래스에 moveAnimal() 메서드를 만들었다.
이 메서드는 어떤 인스턴스가 매개변수로 넘어와도 모두 Animal 형으로 변환한다.
예를 들어 매개변수가 전달되는 부분에 Human 인스턴스가 전달되었다면 다음 코드처럼 형 변환된다.
Animal ani = new Human();
Animal에서 상속받은 클래스가 매개변수로 넘어오면 모두 Animal형으로 변환되므로 animal.move() 메서드를 호출할 수 있다.
가상 메서드 원리에 따라 animal.move() 메서드가 호출하는 메서드는 Animal의 move가 아닌 매개변수로 넘어온 실제 인스턴스의 메서드이다.
animal.move() 코드는 변함이 없지만 어떤 매개변수가 넘어왔느냐에 따라 출력문이 달라진다.
이것이 바로 다형성이다.
다형성의 장점
다른 동물이 새로 추가되는 경우를 생각해 본다.
새로운 동물도 Animal 클래스를 상속받아 구현하면 모든 클래스를 Animal 자료형 하나로 쉽게 관리할 수 있다.
이것이 다형성을 활용한 프로그램의 확장성이다.
각 자료형에 따라 코드를 다르게 구현한다면 코드는 훨씬 복잡해지고 내용도 길다.
허나 상위 클래스에서 공통 부분의 메서드를 제공하고, 하위 클래스에서는 그에 기반한 추가 요소를 덧붙여 구현하면 코드 양도 줄어들고 유지보수도 편리하다.
또 필요에 따라 상속받은 모든 클래스를 하나의 상위 클래스로 처리할 수 있고 다형성에 의해 각 클래스의 여러 가지 구현을 실행할 수 있으므로 프로그램을 쉽게 확장할 수 있다.
다형성을 잘 활용하면 유연하면서도 구조화된 코드를 구현하여 확장성 있고 유지보수하기 좋은 프로그램을 개발할 수 있다.
다형성을 활용해 VIP 고객 클래스 완성하기
https://seungmin576.tistory.com/54 여기 코드 사용
- 앞에서 제시한 VIP 고객의 혜택을 다형성으로 구현해보자
package polymorphism;
public class Customer {
protected int customerID;
protected String customerName;
protected String customerGrade;
int bonusPoint;
double bonusRatio;
public Customer(){
initCustomer(); //고객 등급과 보너스 포인트 적립률 지정 함수 호출
}
public Customer(int customerID, String customerName){
this.customerID = customerID;
this.customerName = customerName;
initCustomer(); //고객 등급과 보너스 포인트 적립률 지정 함수 호출
}
//생성자에서만 호출하는 메서드이므로 private으로 선언
private void initCustomer(){
customerGrade = "SILVER"; //멤버 변수의 초기화
bonusRatio = 0.01;
}
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;
}
}
기존 Customer 클래스와 달라진 점은 initCustomer() 메서드가 있다.
이 메서드는 클래스의 멤버 변수를 초기화하는데, Customer 클래스를 생성하는 두 생성자에서 공통으로 사용하는 코드이므로 메서드로 분리하여 호출한다.
- 이번에는 VIP 고객 클래스 코드를 수정
package polymorphism;
public class VIPCustomer extends Customer {
private int agentID;
double saleRatio;
public VIPCustomer(int customerID, String customerName, int agentID){
super(customerID, customerName); //상위 클래스 생성자 호출
customerGrade = "VIP";
bonusRatio = 0.05;
saleRatio = 0.1;
this.agentID = agentID;
}
@Override
public int calcPrice(int price){
bonusPoint += price * bonusRatio;
return price - (int)(price * saleRatio);
}
@Override
public String showCustomerInfo() { //고객 정보 출력 메서드 재정의
return super.showCustomerInfo() + "담당 상담원 번호는 " + agentID + "입니다.";
}
public int getAgentID(){
return agentID;
}
}
VIP 고객 클래스에서 calcPrice() 메서드와 showCustomerInfo() 메서드를 재정의했다.
일반 고객 클래스에서 calcPrice() 메서드는 정가를 그대로 반환했지만, VIP 고객 클래스에서는 할인율을 반영한 지불 가격을 반환한다.
또 일반 고객 클래스에서 showCustomerInfo() 메서드는 고객 등급과 이름만 출력했지만, VIP 고객 클래스에서는 담당 상담원 번호까지 출력한다.
- test 코드
package polymorphism;
public class CustomerTest {
public static void main(String[] args){
Customer customerLee = new Customer();
customerLee.setCustomerID(10010);
customerLee.setCustomerName("이순신");
customerLee.bonusPoint = 1000;
System.out.println(customerLee.showCustomerInfo());
Customer customerKim = new VIPCustomer(10020, "김유신", 123245);
customerKim.bonusPoint = 1000;
System.out.println(customerKim.showCustomerInfo());
System.out.println("==========할인율이랑 보너스포인트 계산============");
int price = 10000;
int leePrice = customerLee.calcPrice(price);
int kimPrice = customerKim.calcPrice(price);
System.out.println(customerLee.getCustomerName() + "님이 " + leePrice + "원 지불하셨습니다.");
System.out.println(customerLee.showCustomerInfo());
System.out.println(customerKim.getCustomerName() + "님이 " + kimPrice + "원 지불하셨습니다.");
System.out.println(customerKim.showCustomerInfo());
}
}

출력 결과를 보면 만원짜리 상품을 구입했을 때 등급에 따라 다른 할인율과 포인트 적립이 이루어진다.
그런데 여기에서 customerLee와 customerKim은 모두 Customer형으로 선언되었고, 고객의 자료형은 Customer형으로 동일하지만 할인율과 보너스 포인트는 각 인스턴스의 메서드에 맞게 계산되었다.
즉 상속 관계에 있는 상위 클래스와 하위 클래스는 같은 상위 클래스 자료형으로 선언되어 생성할 수 있지만 재정의된 메서드는 각각 호출될 뿐만 아니라 이름이 같은 메서드가 서로 다른 역할을 구현하고 있음을 알 수 있다.
'JAVA' 카테고리의 다른 글
| 다운 캐스팅, instanceof (1) | 2024.12.23 |
|---|---|
| 다형성 활용하기 (0) | 2024.12.23 |
| 메서드 오버라이딩, 가상 메서드 (1) | 2024.12.20 |
| 상속에서 클래스 생성과 형 변환, super() (0) | 2024.12.19 |
| 상속 (1) | 2024.12.19 |