[Java] 다운 캐스팅과 instanceof
상위 클래스로 형 변환되었던 하위 클래스를 다시 원래 자료형으로 형 변환하는 것을
다운 캐스팅(down casting) 이라고 한다.
instanceof
상속관계를 보면 모든 인간은 동물이지만 모든 동물이 인간은 아니다.
따라서 다운 캐스팅을 하기 전에 상위 클래스로 형 변환된 인스턴스의 원래 자료형을 확인해야
변환할 때 오류를 막을 수 있다. 이것을 확인하는 예약어가 ' instanceof ' 다.
Animal hAnimal = new Human();
if(hAnimal instanceof Human // hAnimal 인스턴스의 자료형이 Human형이라면
Human human = (Human)hAnimal; // 인스턴스 hAnimal을 Human형으로 다운캐스팅
}
참조 변수 hAnimal은 원래 Human형으로 생성되었지만 Animal형으로 형 변환되었다.
instanceof 예약어는 왼쪽에 있는 변수의 원래 인스턴스형이 오른쪽 클래스 자료형인가를 확인한다.
instanceof의 반환 값이 true면 다운 캐스팅을 한다. 이때 Human human = (Human)hAnimal; 과 같이
명시적으로 자료형을 써줘야 한다. 상위 클래스로는 묵시형으로 형 변환이 되지만 하위 클래스로
형 변환을 할 때는 명시적으로 해야한다.
원래 자료형이 Human형이 아닌 경우
Animal ani = new Tiger();
Human h = (Human)ani;
이렇게 코딩해도 컴파일 오류는 나지 않는다. 이유는? Tiger 인스턴스는 Animal형으로 자동 형 변환이 된다.
변수 h의 자료형 Human과 강제 형 변환되는 ani의 (Human) 형이 동일하기 때문에 컴파일 오류는 나지 않는다.
이 코드를 실행하면 실행 오류가 발생한다.
아래 예제로 확인해보자
import java.util.ArrayList;
class Animal{ // 상위 클래스 Animal
public void move() {
System.out.println("동물이 움직입니다.");
}
}
class Human extends Animal{ // Animal을 상속받은
public void move() { // Human 클래스
System.out.println("사람이 두발로 걷습니다.");
}
public void readBook() {
System.out.println("사람이 책을 읽습니다.");
}
}
class Tiger extends Animal{ // Animal을 상속받은
public void move() { // Tiger 클래스
System.out.println("호랑이가 네발로 뜁니다.");
}
public void hunting() {
System.out.println("호랑이가 사냥을 합니다.");
}
}
class Eagle extends Animal{ // Animal을 상속받은
public void move() { // Eagle 클래스
System.out.println("독수리가 하늘을 날읍니다.");
}
public void flying() {
System.out.println("하늘을 날아갑니다.");
}
}
public class AnimalTest {
ArrayList<Animal> aniList = new ArrayList<Animal>();
// 배열의 자료형을 Animal로 지정 했다.
public static void main(String[] args) {
AnimalTest test = new AnimalTest();
test.addAnimal();
System.out.println("===원래 형으로 다운 캐스팅===");
test.testCasting();
}
public void addAnimal() {
aniList.add(new Human()); // ArrayList에 추가되면서
aniList.add(new Tiger()); // Animal형으로 형 변환
aniList.add(new Eagle());
for(Animal ani: aniList) { // 배열 요소를 Animal형으로 꺼내서
ani.move(); // move()를 호출하면 재정의된 함수가 호출된다.
}
}
public void testCasting() {
for(int i=0; i<aniList.size(); i++) { // 모든 배열 요소를 하나씩 돌면서
Animal ani = aniList.get(i); // Animal형으로 가져온다.
if(ani instanceof Human) { // animal이 Human인지 확인
Human human = (Human)ani; // Human이면 Human형으로 다운 캐스팅한다.
human.readBook();
}
else if(ani instanceof Tiger) {
Tiger tiger = (Tiger)ani;
tiger.hunting();
}
else if(ani instanceof Eagle) {
Eagle eagle = (Eagle)ani;
eagle.flying();
}
else {
System.out.println("지원되지 않는 기능입니다.");
}
}
}
}
배열의 요소가 Animal형이므로 각 클래스에서 제공하는 readBook(), hunting(), flying() 메서드를
사용할 수 없다. 자료형이 Animal인 상태이기 때문이다. readBook(), hunting(), flying()을 호출하려면
원래 자료형으로 다운 캐스팅되어야 한다.
출력결과