[Java] 5. 5 안전한 하향 캐스팅을 위한 예약어 instanceof

5. 5 안전한 하향 캐스팅을 위한 예약어 instanceof

프로그래밍 언어에서 캐스팅은 형식을 변환하는 것을 말합니다. Java 언어에서 상속 문법을 효과적으로 사용할 수 있게 기반 형식의 변수로 파생 형식 개체를 참조할 수 있게 해 주는데 이를 상향 캐스팅이라 말합니다. 따라서 java 언어에서 상향 캐스팅은 묵시적으로 지원하는 것입니다.

예를 들어 기반 클래스 Musician이 있고 Musician에서 파생한 Pianist 클래스가 있을 때 기반 클래스 Musician 형식 변수 musician에 Pianist 개체를 생성하여 대입하는 것은 매우 자연스러운 코드입니다.

Musician musician = new Pianist();

하지만 반대로 Musician 형식 변수에 참조하는 개체를 파생 클래스 Pianist 형식 변수에 대입하는 것은 묵시적으로 허용하지 않습니다. 이를 허용하지 않는 이유는 자명합니다. 예를 들어 Musician 기반 형식에서 파생한 형식이 Pianist와 Drummer가 있다고 가정합시다. 그리고 Musician 형식 변수 musician에 Drummer 개체를 참조합니다. 이 때 misician 변수를 Pianist 형식 변수에 대입한다면 논리적으로 모순이 발생합니다. 이러한 이유로 하향 캐스팅은 묵시적으로 허용하지 않습니다.

[소스 5.11] 묵시적 하향 캐스팅 오류 예

//기반 클래스
public abstract class Musician {
	public abstract void Play();
}
//파생 클래스 Drummer
public class Drummer extends Musician {
	@Override
	public void Play() {
		System.out.println("두두둥");
	}
}
//파생 클래스 Pianist
public class Pianist extends Musician {
	@Override
	public void Play() {
		System.out.println("딩동댕");
	}
	public void Tunning(){
		System.out.println("도도 레레 미미 파파 솔솔");
	}
}
//묵시적 하향 캐스팅 오류 예
public class Program {
	public static void main(String[] args){
		Musician musician = new Drummer();
		musician.Play();
		
		Pianist pianist = musician;
		pianist.Tunning();		
	}
}

실행 결과

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Type mismatch: cannot convert from Musician to Pianist

	at Program.main(Program.java:8)

그런데 위의 예처럼 파생 클래스 Pianist에서만 동작하는 메서드가 있을 때 기반 형식으로 참조하는 Painist 개체의 해당 메서드(예제 코드의 Tunning)를 호출할 수 없다면 구현의 한계가 발생할 것입니다.

Java 언어에서는 안전하게 참조하는 개체가 특정 형식인지 확인하는 instanceof 예약어를 제공하고 있습니다. 그리고 원하는 형식인지 확인 후에 명시적으로 캐스팅 연산을 사용하여 하향 캐스팅한다면 안전하게 하향 캐스팅할 수 있습니다.

instanceof 예약어는 개체 instanceof 확인할 클래스명을 통해 논리값을 반환합니다. 그리고 명시적 캐스팅은 (변환할 형식명) 개체 형태로 나타냅니다. 다음은 musician 변수에 참조한 개체가 Pianist 형식 개체인지 확인한 후에 명시적으로 하향 캐스팅하는 예입니다.

if(musician instanceof Pianist)

{

Pianist pianist = (Pianist)musician;

pianist.Tunning();

}

[소스 5.12] 안전한 하향 캐스팅 예

//기반 클래스
public abstract class Musician {
	public abstract void Play();
}
//파생 클래스 Drummer
public class Drummer extends Musician {
	@Override
	public void Play() {
		System.out.println("두두둥");
	}
}
//파생 클래스 Pianist
public class Pianist extends Musician {
	@Override
	public void Play() {
		System.out.println("딩동댕");
	}
	public void Tunning(){
		System.out.println("도도 레레 미미 파파 솔솔");
	}
}
//안전한 하향 캐스팅 예
public class Program {
	static void foo(Musician musician){
		if(musician instanceof Pianist){
			Pianist pianist = (Pianist)musician;			
			pianist.Tunning();
		}
		musician.Play();
	}
	public static void main(String[] args){
		Musician musician = new Drummer();
		foo(musician);
		musician = new Pianist();
		foo(musician);
	}
}

실행 결과

두두둥
도도 레레 미미 파파 솔솔
딩동댕