본문 바로가기
BackEnd/Java

[Java] 다형성(Polymorphism) 정리

by ryuneng 2025. 1. 15.
반응형

# 목적

: 학습을 위한 다형성 개념 정리


다형성(polymorphism)이란?

: 여러 가지 형태를 가질 수 있는 능력,
조상 타입 참조 변수로 자손 타입 객체를 다루는 것.

다형성은 백마디 말보다 코드로 보는 게 더 이해하기 쉽다.
작성한 예시의 주석을 참고하며 알아보자.


1. 기본 예시

class Tv {
	boolean power; // 전원상태(on/off)
	int channel;   // 채널
	
	void power() { power = !power; }
	void channelUp() { ++channel; }
	void channelDown() { --channel; }
}

class SmartTv extends Tv {
	String text; // 캡션(자막)을 보여주기 위한 문자열
	void caption() { /* 내용생략 */ }
}

public class Polymorphsim {
	Tv t = new SmartTv(); // 타입 불일치하지만 가능 -> 다형성
//: Tv 리모컨(조상타입의 참조변수)으로 SmartTv 제품(자손타입 객체)을 다룸
}


2. 참조변수의 형변환

: 참조변수(리모컨)을 변경함으로써 사용할 수 있는 멤버의 개수를 조절하는 것
조상, 자손 관계의 참조변수는 서로 형변환 가능 (형제는 불가)

class Car { 					// 멤버 : 4개
	String color;
	int door;
	
	void drive() {
		System.out.println("drive, Brrrr~");
	}
	
	void stop() {
		System.out.println("stop");
	}
}

class FireEngine extends Car {	// 멤버 : 5개 (부모 class인 Car의 멤버까지 사용 가능)
	void water() {
		System.out.println("water!!!");
	}
}

class Ambulance extends Car { /* 생략 */ }

public class Poly {
	public static void main(String[] args) {
		FireEngine f = new FireEngine();
		
		Car c = f;					   // 조상인 Car타입으로 형변환(생략가능)
		FireEngine f2 = (FireEngine)c; // 자손인 FireEngine타입으로 형변환(생략불가)
//		Ambulance a = (Ambulance)f;	   // Error - 상속관계가 아닌 클래스 간의 형변환 불가
		
		f.water();
//		c.water(); // Car타입의 참조변수로는 water() 호출 불가
				   // - water()는 자식인 FireEngine에 있고, Car에는 없음	
	}
}


3. instanceof 연산자

: 참조변수의 형변환 가능 여부 확인에 사용 (가능하면 true 반환)
형변환 전에 반드시 instanceof로 확인해야 함

public class Instanceof {
	void doWork(Car c) {
		if (c instanceof FireEngine) {		// 1. 형변환이 가능한지 확인
			FireEngine fe = (FireEngine)c;  // 2. 형변환
			fe.water();
			// * 여기서 형변환을 하는 이유 : 인스턴스의 원래 기능을 모두 사용하기 위해.
			// - Car타입의 리모컨인 c로는 water() 호출 불가하니, 리모컨을 FireEngine타입으로 바꿔서 호출
		}
	}
}


4. 매개변수의 다형성

: 참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.

class Product {
	int price;		// 제품가격
	int bonusPoint; // 제품 구매 시 제공하는 보너스점수
	
	public Product(int price) {
		this.price = price;
		bonusPoint = (int)(price/10.0); // 보너스점수는 제품가격의 10%
	}
}

class Tv extends Product {
	public Tv() {  // 조상클래스의 생성자 Product(int price) 호출
		super(100); // -> Tv의 가격을 100만원으로 한다.
	}
	
	// Objct클래스의 toString()을 오버라이딩한다.
	public String toString() { return "Tv"; }
}

class Computer extends Product {
	public Computer() { super(200); }
	public String toString() { return "Computer"; }
}

class Buyer { // 물건 사는 사람
	int money = 1000;	// 소유금액
	int bonusPoint = 0; // 보너스점수
	
    // Tv, Computer 매개변수 메서드를 따로 만들지 않고 (오버로딩하지 않고) Product 하나로 가능
	void buy(Product p) { 
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		money -= p.price;			// 가진 돈에서 구입한 제품의 가격을 뺀다.
		bonusPoint += p.bonusPoint; // 제품의 보너스 점수를 추가한다.
		System.out.println(p + "을/를 구입하셨습니다.");
//		System.out.println(p.toString() + "을/를 구입하셨습니다.");
		// 위와 동일 (p = p.toString())
	}
}

public class Poly {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		
//		Product p = new Tv(); // 클래스 형변환
//		b.buy(p);			  // 이 두 줄을 아래 한 줄로 간단히 -> b.buy(new Tv());
		b.buy(new Tv());	   	// buy(Product p)
		b.buy(new Computer()); 	// buy(Product p)
		
		System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
		System.out.println("현재 보너스 점수는 " + b.bonusPoint + "점입니다.");
	}
}


이쯤에서 알아보는 다형성의 장점

  1. 다형적 매개변수
  2. 하나의 배열로 여러 종류 객체 다루기


# 종합 예시

class Product {
	int price;		// 제품가격
	int bonusPoint; // 제품 구매 시 제공하는 보너스점수
	
	Product(int price) {
		this.price = price;
		bonusPoint = (int)(price/10.0); // 보너스점수는 제품가격의 10%
	}
	
	Product() {	} // 기본 생성자
}

class Tv extends Product {
	public Tv() { super(100); }
	public String toString() { return "Tv"; }
}

class Computer extends Product {
	public Computer() { super(200); }
	public String toString() { return "Computer"; }
}

class Audio extends Product {
	public Audio() { super(50); }
	public String toString() { return "Audio"; }
}

class Buyer { // 물건 사는 사람
	int money = 1000;	// 소유금액
	int bonusPoint = 0; // 보너스점수
	Product[] cart = new Product[10]; // 구입한 제품을 저장하기 위한 배열
	int i = 0;			// Product배열에 사용될 카운터
	
	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살 수 없습니다.");
			return;
		}
		
		money -= p.price;			// 가진 돈에서 구입한 제품의 가격을 뺀다.
		bonusPoint += p.bonusPoint; // 제품의 보너스 점수를 추가한다.
		cart[i++] = p;				// 제품을 Product[] cart에 저장한다.
		System.out.println(p + "을/를 구입하셨습니다.");
	}
	
	void summary() {				// 구매한 물품에 대한 정보를 요약해서 보여준다.
		int sum = 0;				// 구입한 물품의 가격 합계
		String itemList = "";		// 구입한 물품 목록
		
		// 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
		for(int i=0; i<cart.length; i++) {
			if(cart[i]==null) break;
			sum += cart[i].price;
			itemList += cart[i].toString() + ", ";
		}
		System.out.println("구입하신 물품의 총 금액은 " + sum + "만원입니다.");
		System.out.println("구입하신 제품은 " + itemList + "입니다.");
	}
}

public class Poly_Ex {
	public static void main(String[] args) {
		Buyer b = new Buyer();
		
		b.buy(new Tv());
		b.buy(new Computer());
		b.buy(new Audio());
		b.summary();
	}
}




* 클래스 형변환

- 상속/구현객체에 있는 클래스(객체)에서 부모타입의 참조변수로 자식객체를 참조할 수 있다.

* 클래스 형변환과 매개변수 다형성 의 차이점 ?
클래스 형변환은 일어나는 현상,
매개변수 다형성은 클래스 형변환이 가능하기 때문에 획득되는 이점이라고 할 수 있다.

* 메소드 재정의

- 부모로부터 물려받은 기능을 자신의 클래스에 맞게 재정의
- 추상화된 기능을 구현하기 -> 사용방법이 동일한 구현클래스의 제작이 가능



* 참고 : 유튜브 [자바의 정석 기초]


 


< 해당 글은 velog에서 이전하며 옮겨온 글로, 가독성이 좋지 않을 수 있는 점 양해 부탁드립니다. >

🔗 velog 버전 보기 : https://velog.io/@ryuneng2/Java-다형성-Polymorphism