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. 상속 관계가 아닌 클래스 간 형변환 불가
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
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];
}