[Java] Java의 정석 정리 (다형성1)

송병훈·2022년 9월 27일
0

자바의 정석

목록 보기
10/15
post-thumbnail

상속부터 다시 볼게요.

상속 : 부모클래스의 멤버(변수, 메소드)들을
자손클래스가 물려받아 사용하는 것.
자손클래스 자체 멤버를 만들면서 확장(extends)됨

다형성의 의미는 '여러 형태가 존재한다' 입니다.
Java에서는 무슨 의미일까요?

"무엇이 어떻게 여러 형태를 지니는 거야?"
"그 뭔가가 여러 형태를 지녀서 어디에 써먹어?"

이런 궁금증이 듭니다. 한 번 알아보죠.


다형성 (Polymorphism)

우선, 자손클래스의 인스턴스를 생성할 때
부모클래스의 참조변수에 주소를 저장할 수 있습니다.

그리고 다형성을 간단히 말하면 이렇습니다.
"하나의 인스턴스를 다루는 참조변수가 여러 개다"
"하나의 참조변수로 여러 인스턴스를 다룰 수 있다"

둘 다 말이 됩니다.
하나씩 보죠.


1. "하나의 인스턴스를 다루는 참조변수가 여러 개다"

class Product { /* 2개의 멤버 */ }
class Tv extends Product { /* 2개의 멤버 */ }
class SmartTv extends Tv { /* 2개의 멤버 */ }

SmartTv s = new SmartTv();	// SmartTv 인스턴스 생성 - 총 6개의 멤버
Tv t = (Tv)s;				// 부모인 Tv 클래스로 형변환 - 인스턴스에는 6개의 멤버가 있지만, 4개의 멤버만 다룸
Product p = (Product)t;		// Tv보다 부모인 Product 클래스로 형변환  - 인스턴스에는 6개의 멤버가 있지만, 2개의 멤버만 다룸
SmartTv s2 = (SmartTv)p;	// SmartTv 클래스로 형변환 - 다시 6개의 멤버를 다룰 수 있음
// 인스턴스는 하나이므로 각 참조변수에 저장되는 주소는 동일함

그림을 보면, SmartTv 인스턴스 하나를
Product p / Tv t / SmartTv s
여러 타입의 참조변수가 다루고 있죠?

부모-자식 간 참조변수의 형변환이 이뤄지면서
각 참조변수가 다룰 수 있는 멤버의 개수가 달라집니다.

이때 인스턴스에 수많은 멤버들이 있어도
부모클래스의 참조변수를 통해서는
부모클래스까지 상속받은 멤버들만 다룰 수 있고
자손클래스 고유의 멤버는 다루지 못합니다.


2. "하나의 참조변수로 여러 인스턴스를 다룰 수 있다"

기본적으로 자기자신 클래스의 참조변수에
자기자신 클래스의 인스턴스 주소를 저장하죠.

그리고 위에서, 조상클래스의 참조변수에
자손클래스의 인스턴스 주소를 저장할 수 있다고 했어요.

class Product { /* 2개의 멤버 */ }
class Tv extends Product { /* 2개의 멤버 */ }
class SmartTv extends Tv { /* 2개의 멤버 */ }

Product p1 = new Product();	// 자신의 인스턴스
Product p2 = new Tv();		// 자손 인스턴스
Product p3 = new SmartTv();	// 자손 인스턴스

--
이 특성을 활용해보죠.

어떤 메소드의 매개변수로 조상클래스의 참조변수가 있으면
이 매개변수의 인자로는 자손클래스의 인스턴스가 넘어올 수 있겠죠.

자손클래스가 여러 개라면
여러 자손클래스의 인스턴스를 다룰 수 있어요.

아래 코드에서 확인해봅시다.

class PolyArgumentTest {
	public static void main(String args[]) {	// main 메소드
		Buyer b = new Buyer();
		
        /* Buyer클래스의 buy(Product p) 메소드 호출.
		 * 매개변수로는 부모클래스가, 
         * 인자로는 자손 인스턴스가 있다. 
		 */
        b.buy(new Tv());		// Product p = new Tv();
		b.buy(new Computer());	// Product p = new Computer();

		System.out.println("현재 남은 돈은 " + b.money + "만원입니다.");
		System.out.println("현재 보너스점수는 " + b.bonusPoint + "점입니다.");
	}
}

class Product {
	int price;			// 제품의 가격
	int bonusPoint;		// 제품구매 시 제공하는 보너스점수

	Product(int price) {
		this.price = price;
		bonusPoint =(int)(price/10.0);	// 보너스점수는 제품가격의 10%
	}
}

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

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

class Buyer {			// 고객, 물건을 사는 사람
	int money = 1000;	// 소유금액
	int bonusPoint = 0;	// 보너스점수

	void buy(Product p) {
		if(money < p.price) {
			System.out.println("잔액이 부족하여 물건을 살수 없습니다.");
			return;
		}

		money -= p.price;			// 가진 돈에서 구입한 제품의 가격을 뺀다.
		bonusPoint += p.bonusPoint;	// 제품의 보너스 점수를 추가한다.
		System.out.println(p + "을/를 구입하셨습니다.");	// toString() 메소드 호출함
	}
}

부모클래스 Product와 자손클래스 Tv, Computer.
하단의 Buyer클래스의 buy()메소드.
main메소드에서 b.buy()메소드 호출을 봅시다.

부모클래스인 Product p 를 매개변수로 했고
자손클래스의 인스턴스 new Tv(), new Computer()
인자로 했어요.

이렇게 매개변수 - 인자 관계를 통해
하나의 (부모클래스) 참조변수로
여러 (자손클래스) 인스턴스를 다룰 수 있습니다.

이렇게 되면 여러 자손클래스 각각에
동일한 변수, 메소드를 작성할 필요가 없어져
중복이 줄어들죠.


오늘도 고생하셨어요 :)

profile
성실하고 꼼꼼하게

0개의 댓글