다형성 Polymorphism이란?
한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있는 것을 말합니다.
부모 클래스 타입의 참조변수로 자식 클래스의 인스턴스를 참조할 수 있습니다.
다형성은 상속, 추상화와 더불어 객체 지향 프로그래밍을 구성하는 중요한 특징 중 하나입니다.
자바에서는 다형성을 통해서 부모 클래스 타입의 참조변수로 자식 클래스의 인스턴스를 참조할 수 있습니다.
참조변수가 사용할 수 있는 멤버의 개수는 참조하는 인스턴스의 멤버 개수보다 같거나 적어야 합니다.
조상 클래스의 멤버 개수는 자식 클래스의 멤버 개수보다 항상 적거나 같습니다.
즉, 자식 클래스 타입의 참조변수로 부모 클래스의 인스턴스를 참조할 수 없습니다.
참조변수의 타입이 참조변수가 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 개수를 결정합니다.
class TV {
boolean power;
int channel;
void power(){
power = !power;
}
void channelUp() {
++channel;
}
void channelDown() {
--channel;
}
}
class CaptionTv extends Tv {
String text;
void caption() {
...
}
}
class CastingTest {
//참조변수 타입 = 인스턴스의 타입
Tv t = new TV();
CaptionTv c = new CaptionTv();
//참조변수 타입 != 인스턴스의 타입
//부모 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조
Tv t = new CaptionTv();
}
- CaptionTv 타입의 인스턴스를 참조하는 Tv 타입의 참조변수(t)와 CaptionTv 타입의 참조변수(c)의 차이점
t는 Tv 클래스의 멤버들은 모두 사용할 수 있지만, CaptionTv 인스턴스의 멤버 중에서 Tv 클래스에 정의 되지 않은 멤버인 text와 caption()은 사용할 수 없습니다.
그러나, c는 Tv 클래스와 CaptionTv 인스턴스의 모든 멤버를 사용할 수 있습니다.
* 자식 클래스 타입의 참조변수가 부모 클래스 타입의 인스턴스를 참조할 수 없는 이유
CaptionTv c = new Tv(); //컴파일 에러 발생!!
실제 인스턴스인 Tv의 멤버 개수보다 참조변수 c가 사용할 수 있는 멤버 개수가 더 많기 때문에, 위 코드를 컴파일하면 에러가 발생합니다.
c.text나 c.caption()과 같이 자식 클래스에만 존재하는 멤버를 사용하면 실제 인스턴스는 부모 클래스로부터 생성되었기 때문에 문제가 발생하기 때문입니다.
따라서, 참조변수가 사용할 수 있는 멤버의 개수는 참조하는 인스턴스의 멤버 개수보다 같거나 적어야 합니다.
- 조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 경우 vs 자손 클래스 타입의 참조변수로 자손 인스턴스를 참조하는 경우
조상 클래스의 메서드를 자손 클래스에서 오버라이딩한 경우에도 참조변수의 타입에 관계없이 항상 자손 인스턴스의 메서드가 호출됩니다.
멤버변수의 경우에는 조상 클래스와 자손 클래스에 중복으로 정의된 경우에 조상 타입의 참조변수를 사용할 때에는 조상 클래스에 선언된 멤버변수가 사용되고, 자손타입의 참조변수를 사용할 때는 자손 클래스에 선언된 멤버변수가 사용됩니다.
참조변수의 형변환
서로 상속관계에 있는 클래스사이에서 참조변수는 형변환이 가능합니다.
- 자식 타입 -> 부모 타입: Up-casting, 형변환 생략이 가능
- 부모 타입 -> 자식 타입: Down-casting, 형변환 생락이 불가능
자바에서는 부모와 자식관계만 존재하기 때문에 형제 클래스는 서로 아무런 관계가 없으므로 형제 클래스간 캐스팅은 불가합니다.
class Car {
String color;
int door;
void drive() {
System.out.println("drive, Errr~");
}
void stop() {
System.out.println("stop!!!");
}
}
class FireEngine extends Car {
void water() {
System.out.println("water!!!");
}
}
class Ambulance extends Car {
void siren() {
System.out.println("siren~~~");
}
}
class CastingTest {
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
car = fe; //car = (Car)fe; 업 캐스팅
fe2 = (FireEngine)car; //다운 캐스팅
}
FireEngine 타입의 참조변수 fe를 Car 타입의 참조변수 car로 업 캐스팅하는 경우에는 fe 참조변수가 다룰 멤버 개수가 줄어들기 때문에 형변환을 생략할 수 있습니다.
Car 타입의 참조변수 car을 FireEngine 타입의 참조변수 fe2로 다운 캐스팅하는 경우에는 car 참조변수가 다룰 멤버 개수가 늘어나기 때문에 문제가 발생할 가능성이 있습니다.
따라서 다운 캐스팅(조상 -> 자식)인 경우, 형변환은 생략될 수 없습니다.
형변환은 참조변수의 타입을 변환하는 것이며, 인스턴스를 변환하는 것이 아닙니다.
즉, 참조변수의 형변환은 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위를 조절하기 위해 쓰입니다.
instanceof 연산자
참조변수의 캐스팅을 진행할 때 참조변수가 가리키는 인스턴스의 타입이 무엇인지 확인해야 합니다.
참조변수가 참조하고 있는 인스턴스가 존재할 때, 해당 인스턴스의 자손타입으로 형변환이 불가하기 때문입니다.
따라서, 런타임에 참조변수가 참조하고 있는 실제 인스턴스의 타입을 확인하기 위해 instanceof 연산자를 사용합니다.
"참조변수 instanceof 타입"의 연산의 결과는 참조변수가 검사한 타입으로 형변환이 가능하면 true를 반환하고, 형변환이 불가하면 false를 반환합니다.
참조변수의 실제 인스턴스의 타입의 조상 타입이거나 같은 타입일 때 instanceof 연산은 true이며, 형변환이 가능하다는 것을 의미합니다.
class InstanceofText() {
public static void main(String args[]) {
FireEngine fe = new FireEngine();
if (fe instanceof FireEngine) {
System.out.println("This is a FireEngine instance.");
}
if (fe instanceof Car) {
System.out.println("This is a Car instance.");
}
if (fe instanceof Object) {
System.out.println("This is a Object instance.");
}
}
}
//출력
This is a FireEngine instance.
This is a Car instance.
This is a Object instance.
매개변수의 다형성
참조변수의 다형적인 특징은 메서드의 매개변수에도 적용됩니다.
매개변수가 조상 클래스 타입의 참조변수인 경우, 자손 클래스 타입의 참조변수이면 매개변수로 받아들일 수 있습니다.
class Product {
int price;
int point;
Product(int price) {
this.price = price;
point = (int)(price/10.0);
}
}
class Tv extends Product {
Tv() {
super(10);
}
}
class Computer extends Product {
Computer() {
super(200);
}
}
class Audio extends Product {
Audio() {
super(50);
}
}
class Buyer {
int money = 1000;
int point = 0;
Product[] item = new Product[10];
int i = 0;
void buy(Product p) {
if(money < p.price) {
System.out.println("잔액 부족");
return;
}
money -= p.price;
point += p.point;
item[i++] = p;
}
}
class PolyArgumentTest {
public static void main(String args[]) {
Buyer b = new Buyer();
b.buy(new Tv());
b.buy(new Computer());
b.buy(new Audio());
}
}
참고
- 참고 도서: Java의 정석 - 남궁성 지음
'Java' 카테고리의 다른 글
내부 클래스 Inner Class (0) | 2022.05.24 |
---|---|
추상화 Abstraction (0) | 2022.05.24 |
제어자 Modifier (0) | 2022.05.20 |
super와 this (0) | 2022.05.20 |
상속 Inheritance (0) | 2022.05.20 |