본문 바로가기
JAVA

19. 다형성, 참조변수의 형변환

by seongju.lee 2022. 11. 10.

다형성

  • 조상 타입 참조변수로 자손 타입 객체를 다루는 것이다.
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()이라는 메서드는 사용할 수 없지만 말이다.)
    • 반대로 자식타입으로 참조변수를 선언하고,
      부모타입의 인스턴스를 참조하는 것을 불가능하다.
      (당연히 더 많은 멤버를 가지고 있을 수 있기 때문이다.)
      • 자식이 부모타입의 인스턴스를 참조하는 것을 불가하기 때문에,
        부모타입의 객체를 자식타입으로 형변환을 해주고 사용해야 한다.

 

참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절하는 것이라고 할 수 있다.
    (어차피 동일한 인스턴스 내에서 동일한 데이터를 쓰는 것인데, 그러한 데이터를 사용할 수 있냐 없냐의 차이.)
  • 조상 <-> 자손 관계의 참조변수에서 서로 형변환이 가능하다.
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과 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가 동일한 것을 볼 수 있다.

 

 

 

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