[Java] 다형성과 형변환

준우·2023년 2월 20일
0

Java

목록 보기
29/30
post-thumbnail

다형성(polymorphism)

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

예시 코드

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()	{ /* 내용 생략 */ }
}

위와 같이 Tv 클래스와, Tv 클래스를 상속 받은 SmartTv 클래스가 있을 때

SmartTv s = new SamrtTv();	// 참조 변수와 인스턴스 타입이 일치
Tv 		t = new SmartTv();	// 조상 타입 참조변수로 자손 타입 인스턴스 참조

SmartTv s = new SamrtTv(); 처럼 참조변수와 인스턴스 타입이 일치하는 경우가 일반적이다.
그런데 Tv t = new SmartTv(); 처럼 참조변수와 인스턴스 타입이 불일치하지만
조상타입의 참조변수로 자손 타입 객체를 다루는 것이 가능한 게 다형성이다.

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()	{ /* 내용 생략 */ }
}

다시 Tv와 SmartTv 클래스가 정의된 코드를 보면,Tv의 멤버는 5개이고, SmartTv의 멤버는 7개이다.

Tv 	t = new SmartTv();	// 조상 타입 참조변수로 자손 타입 인스턴스 참조

그런데 위와 같이 선언했을 때, 참조변수 t가 SmartTv 인스턴스를 참조한다고 하더라도
t는 Tv 타입으로 선언되었으므로 Tv의 멤버만 사용할 수 있다.
SmartTv의 text나, caption() 멤버는 사용할 수 없다.

왜냐하면 그림의 오른쪽처럼 Tv 리모컨은 5가지 멤버만 가지고 있기 때문에, 7가지 기능이 있어도 사용할 수 없는 것이다.

주의점

Tv		t = new SmartTv();	// OK. 
SmartTv s = new Tv();		// Error.
  • 자손 타입의 참조변수로는 조상 타입의 객체를 가리킬 수 없다.

위 그림을 예시로 들면, Tv리모컨에는 기능이 7개여도 버튼 개수가 5개라 사용할 수 있는 기능도 5개뿐인 것은 괜찮지만
SmartTv 리모컨의 버튼이 7개인데, 사용할 수 있는 기능이 5개뿐인 것은 괜찮지 않다는 것이다.

참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절하는 것 (주소값, 객체는 바뀌지 않는다.)
  • 조상-자손 관계의 참조변수는 서로 형변환 가능
class Car{	}
class FireEngine extends Car {	}
class Ambulance  extends Car {	}
FireEngine f  = new FireEngine();	
Car 	   c  = (car)f;				// 업캐스팅
FireEngine f2 = (FireEngine)c;		// 다운캐스팅
Ambulance  a  = (Ambulance)f;		// Error. 상속 관계가 아닌 클래스 간 형변환 불가

instanceof 연산자

FireEngine fe = new FireEngine();
System.out.println(fe instanceof Object);		// true
System.out.println(fe instanceof Car);			// true
System.out.println(fe instanceof FireEngine);	// true
  • 참조변수의 형변환 가능여부 확인에 사용한다.
  • 형변환이 가능하면 true, 불가하면 false를 반환한다.
  • 형변환 전에 반드 시 instanceof로 확인해야 한다.
void doWork(Car c) {
	if (c instanceof FireEngine) {		// 1. 형변환이 가능한지 확인
    	FireEngine fe = (FireEngine)c;	// 2. 형변환
        fe.water();
        ...
    }
}

형변환을 하는 이유는 인스턴스의 원래 기능을 모두 사용하기 위해서이다.
Car 타입의 리모콘인 c로는 water()을 호출할 수 없으니까
리모콘을 FireEngine 타입으로 바꿔서 water()을 호출한다.

다형성의 장점

매개변수의 다형성

  • 참조형 매개변수는 메서드 호출 시, 자신과 같은 타입 또는 자손타입의 인스턴스를 넘겨줄 수 있다.
class Product {
	int price;
    int bonusPoint;
}

class Tv extends Product {}
class Computer extends Product {}
class Audio extends Product {}

class Buyer {
	int money = 100000;
    int bonusPoint = 0;
}

위와 같은 코드가 있을 때, 아래 buy() 메서드에 Tv를 매개변수로 받는다면 컴퓨터나 오디오를 살때도 아래와 같이 오버로딩을 해야할 것이다. 혹은 상품이 추가될 때마다 오버로딩이 필요할 것이다.

void buy(Tv t) {
	money -= t.price;
    bonusPoint += t.bonusPoint;
}

void buy(Computer c) {
	money -= c.price;
    bonusPoint += c.bonusPoint;
}

void buy(Audio a) {
	money -= a.price;
    bonusPoint += a.bonusPoint;
}
Buyer b = new Buyer();
Tv tv = new Tv();
Computer com = new Computer;

b.buy(tv);
b.buy(com);

그런데 이때 매개변수를 Product로 넣는다면 코드가 간단해진다.

void buy(Product p) {
	money -= p.price;
    bonusPoint += p.bonusPoint;
}
Buyer b = new Buyer();
Product tv = new Tv();
Product com = new Computer;

b.buy(tv);
b.buy(com);

하나의 배열에 여러 종류 객체 저장

  • 원래 배열에는 한가지 종류의 객체만 저장할 수 있지만
    다형성을 사용하면 조상 타입의 배열에 자손들의 객체를 담을 수 있다.
Product [] new = Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
class Buyer {
	int money = 100000;
    int bonusPoint = 0;
    Product[] cart = new Product[10];
}

🫡 Reference

0개의 댓글