다형성
- 조상 타입 참조변수로 자손 타입 객체를 다루는 것이다.
class Tv {
boolean power;
int channel;
void power() {power = !power; }
void channeUp() {++channel;}
void channelDown() {--channel;}
}
class SmartTv extends Tv {
String text;
void caption() {}
}
public class PolymorphismEx {
public static void main(String[] args) {
// 보통 아래와 같이 타입이 일치함.
Tv t = new Tv();
SmartTv st = new SmartTv();
// 아래와 같은 타입불일치를 다형성이라고 함.
// 조상타입의 참조변수로 자식타입의 인스턴스를 참조함
Tv polyTv = new SmartTv();
}
}
- 위 코드를 살펴보면,
- 부모타입인 Tv class를 사용하여 참조변수 polyTv라는 것을 생성하고,
자식타입인 SmartTv 인스턴스를 참조하였다. - 이렇게 되면 polyTv라는 참조변수에는 총 7개의 멤버를 가지고 있게된다.
(물론, text라는 멤버변수와 caption()이라는 메서드는 사용할 수 없지만 말이다.) - 반대로 자식타입으로 참조변수를 선언하고,
부모타입의 인스턴스를 참조하는 것을 불가능하다.
(당연히 더 많은 멤버를 가지고 있을 수 있기 때문이다.)
- 자식이 부모타입의 인스턴스를 참조하는 것을 불가하기 때문에,
부모타입의 객체를 자식타입으로 형변환을 해주고 사용해야 한다.
- 자식이 부모타입의 인스턴스를 참조하는 것을 불가하기 때문에,
- 부모타입인 Tv class를 사용하여 참조변수 polyTv라는 것을 생성하고,
참조변수의 형변환
- 사용할 수 있는 멤버의 갯수를 조절하는 것이라고 할 수 있다.
(어차피 동일한 인스턴스 내에서 동일한 데이터를 쓰는 것인데, 그러한 데이터를 사용할 수 있냐 없냐의 차이.) - 조상 <-> 자손 관계의 참조변수에서만 서로 형변환이 가능하다.
class Car {
String color;
int door;
void drvie() {}
void stop() {}
}
class FireEngine extends Car {
void water() {}
}
FireEngine child = new FireEngine();
Car parents = (Car)child; // 생략 가능함.
FireEngine child2 = (FireEngine)parents; // 생략 불가능.
- 위 코드에서 맨 아래를 잘 살펴봐야한다.
- upcasting
- 부모타입의 참조변수가 자식타입의 인스턴스를 참조하는 것.
- 이때는 자동형변환이 된다.
- downcasting
- 자식타입의 참조변수가 부모타입의 인스턴스를 참조하려고 하는 것.
- 이때는 명시적으로 형변환을 해줘야한다.
- upcasting
- 즉, 참조변수의 형변환을 통해서 사용할 수 있는 멤버의 갯수를 조절할 수 있는 것인데
여기서 형변환을 하는 종류에는 upcasting과 downcasting이 있는 것이다.
Tv t1 = new Tv();
SmartTv st1 = new SmartTv();
Tv t2 = st1; // upcasting
SmartTv st2 = (SmartTv)t2; // downcasting
- 위에 Tv, SmartTv 클래스를 위와 같이 사용한다고 했을 때,
- t2는 st1을 참조함으로써, 멤버를 5개 사용할 수 있는 것이다.
- st2는 형변환 된 t2를 함으로써 6개의 멤버를 사용할 수 있는 것이다.
참조변수의 형변환 - 주의점
- 참조변수의 형변환은 타입만 명시해주고, 타입만 맞으면 컴파일은 문제가 발생하지 않는다.
- 문제는 자료형을 일치 시키는 것이 아닌 실제 객체가 가리키고 있는 값들이 일치 되어있는 지 파악해야한다.
(동일한 자료형이 아닌, 동일한 클래스를 참조하는지에 따라 캐스트 가능 여부가 결정되기 때문이다.)
Tv t1 = new Tv();
SmartTv st1 = new SmartTv();
SmartTv st2 = (SmartTv)t1;
st2.caption();
- 위의 코드는 t1의 형변환을 통해서 st2의 타입과 일치하기 때문에 컴파일에러는 발생하지 않는다.
- 다만, 실행을 시키면 런타임 에러가 발생할 것이다. 그 이유는 다음과 같다.
- 현재, Tv타입인 t1이라는 객체는 Tv클래스를 가리키고 있다.
- 때문에 실행시키면 Tv라는 클래스는 SmartTv 클래스로 다운캐스팅이 될 수 없는 것이다.
- 아래 디버깅한 결과를 보면 더 직관적이다. (객체의 id가 다른 것을 알 수 있다.)
- 그렇다면, 어떻게 수정해야 Tv -> SmartTv로 형변환이 될 수 있을까?
SmartTv st1 = new SmartTv();
Tv t1 = st1;
SmartTv st2 = (SmartTv)t1;
st2.caption();
- 위 코드처럼 변경하면 문제없이 실행될 것이다.
- t1라는 Tv타입을 가진 참조변수로 SmartTv 객체를 참조함으로써
t1은 SmartTv클래스를 참조하지만 SmartTv의 멤버는 사용할 수 없게된다. - 그리고, t1객체를 SmartTv로 형변환 하면 SmartTv의 멤버를 사용할 수 있을 것이고,
동일한 타입의 참조변수 st2로써 객체를 생성하는 것이다. - 아래 결과를 보면 id가 동일한 것을 볼 수 있다.
- t1라는 Tv타입을 가진 참조변수로 SmartTv 객체를 참조함으로써
instanceof 연산자
void doWork(Tv t) {
if(t instanceof SmartTv) {
SmartTv stv = (SmartTv)t;
}
}
- instanceof 연산자는 형변환이 가능한지 확인하기 위해 사용된다.
- return값은 true or false이다.
- 위 예제에서는 인자가 SmartTv로 형변환이 가능한지 확인하는 용도로 사용 되었다.
- instanceof 뒤에 오는 타입은 t와 동일한 타입 뿐만 아니라 조상 클래스 모두 true로 반환된다.
'JAVA' 카테고리의 다른 글
21. 추상 클래스, 추상 메서드 (0) | 2022.11.11 |
---|---|
20. 다형성의 장점 (0) | 2022.11.10 |
18. 캡슐화 (0) | 2022.10.03 |
17. 제어자 (0) | 2022.10.03 |
16. super()와 super (0) | 2022.10.02 |