이전 글들을 통하여 다형성을 활용하여 업캐스팅(up casting)이 된다는 것을 알게 되었다.
여기에서는 다시 하위 클래스로 형 변환(다운캐스팅)이 되는 과정을 살펴보자.
위와 같은 계층 구조에서 상위 클래스를 자료형으로 선언하는 Animal ani = new Human( ); 코드를 쓸 수 있다.
이때 생성된 인스턴스 Human은 Animal형이다. 이렇게 Animal 형으로 형 변환이 이루어졌을 때는 Animal 클래스에서 선언한 메서드와 멤버 변수만 사용할 수 있다.
다시말해 Human 클래스에 더 많은 메서드가 구현되어 있고 다양한 멤버 변수가 있다고 하더라도 자료형이 Animal형인 상태에서는 사용할 수 없는 것이다.
따라서 필요에 따라 다시 원래 인스턴스의 자료형(여기에서는 Human형)으로 되돌아가야 하는 경우가 있다.
이렇게 상위 클래스로 형 변환되었던 하위 클래스를 다시 원래 자료형으로 형 변환하는 것을 다운캐스팅(down casting)이라고 한다.
instanceof
다운캐스팅을 하기 전에 상위 클래스로 형 변환된 인스턴스의 원래 자료형을 확인해야 변환할 때 오류를 막을 수 있다.
이를 확인하는 예약어로 instanceof가 있다.
instanceof의 사용 방법은 다음과 같다.
Animal hAnimal = new Human( );
if(hAnimal instanceof Human) { // hAnimal 인스턴스 자료형이 Human형이라면
Human human = (Human)hAnimal; // 인스턴스 hAnimal을 Human형으로 다운캐스팅
}
위 코드에서 instanceof 예약어는 왼쪽에 있는 변수의 원래 인스턴스형이 오른쪽 클래스 자료형인지 확인한다.
코드를 보면 hAnimal이 Animal형으로 되어 있지만, 원래는 Human형으로 생성된 인스턴스인지 확인하는 것이다.
instanceof의 반환 값이 true이면 다운캐스팅을 하는데, 이때는 Human human = (Human)hAnimal; 문장과 같이 명시적으로 자료형을 적어주어야 한다.
상위클래스로는 묵시적으로 형 변환이 되지만, 하위 클래스로 형 변환을 할 때는 명시적으로 해야 하기 때문이다.
만약 instanceof로 인스턴스형을 확인하지 않으면 오류가 발생할 수 있다.
다음처럼 원래 자료형이 Human형이 아닌 경우를 보자.
Animal ani = new Tiger( );
Human h = (Human)ani;
Tiger 인스턴스를 Animal형으로 자동으로 형 변환을 하고, 변수 h의 자료형 Human의 강제 형 변환되는 ani의 (Human)형이 동일하다.
이렇게 코딩해도 컴파일 오류는 나지 않는다. 하지만 실행 시에 다음과 같은 오류를 반환한다.
따라서 참조 변수의 원래 인스턴스를 정확히 확인하고 다운캐스팅을 해야 안전하고, 이때 instanceof를 사용한다.
그러면 원래 인스턴스형으로 다운캐스팅 하는 예를 살펴보자.
import java.util.ArrayList;
class Animal {
public void move() {
System.out.println("동물이 움직입니다.");
}
}
class Human extends Animal {
public void move() {
System.out.println("사람이 두 발로 걷습니다.");
}
public void readBook() {
System.out.println("사람이 책을 읽습니다.");
}
}
class Tiger extends Animal {
public void move() {
System.out.println("호랑이가 네 발로 뜁니다.");
}
public void hunting() {
System.out.println("호랑이가 사냥을 합니다.");
}
}
class Eagle extends Animal {
public void move() {
System.out.println("독수리가 하늘을 날읍니다.");
}
public void flying() {
System.out.println("독수리가 날개를 쭉 펴고 멀리 날아갑니다.");
}
}
public class AnimalTest {
ArrayList<Animal> aniList = new ArrayList<Animal>();
public static void main(String[] args) {
Animal ani = new Tiger();
Human h = (Human)ani;
AnimalTest aTest = new AnimalTest();
aTest.addAnimal();
System.out.println("원래 형으로 다운캐스팅");
aTest.testCasting();
}
public void addAnimal() { // ArrayList에 추가되면서 Animal으로 형 변환
aniList.add(new Human());
aniList.add(new Tiger());
aniList.add(new Eagle());
for(Animal ani : aniList) { // 배열 요소를 Animal형으로 꺼내서 move()를 호출
ani.move();
}
}
public void testCasting() {
for(int i=0; i < aniList.size(); i++) { // 모든 배열 요소를 하나씩 순회하면서
Animal ani = aniList.get(i); // Animal형으로 가져옴
if(ani instanceof Human) { // Human이면
Human h = (Human)ani; // Human형으로 다운캐스팅
h.readBook();
}
else if(ani instanceof Tiger) { // Tiger이면
Tiger t = (Tiger)ani; // Tiger형으로 다운캐스팅
t.hunting();
}
else if(ani instanceof Eagle) { // Eagle이면
Eagle e = (Eagle)ani; // Eagle형으로 다운캐스팅
e.flying();
}
else {
System.out.println("지원되지 않는 형입니다.");
}
}
}
}
1. 각 동물 클래스를 인스턴스로 생성해서 Animal형으로 선언한 배열에 추가한다.(추가되면서 자료형은 Animal형으로 변환됨)
2. 이때 호출할 수 있는 메서드는 Animal 클래스에 선언된 메서드(Animal.move( ))뿐이다.
3. 향상된 for문으로 모든 배열 요소를 순회하며 move( )메서드를 호출하여 재정의된 메서드를 호출한다.
4. 하지만 배열 요소가 Animal형이기때문에 각 클래스 별 메서드를 사용할 수 없으므로, instanceof로 다시 원래 자료형으로 다운캐스팅을 한 후 각 클래스에 있는 move( ) 메서드를 호출할 수 있다.
'웹 개발 기초 > 자바 문법' 카테고리의 다른 글
다형성 활용, 상속을 사용하는 경우, IS-A 관계, HAS-A 관계 (0) | 2022.12.07 |
---|---|
가상메서드 (1) | 2022.12.06 |
메서드 오버라이딩, 메서드 오버로딩, 가상 메서드의 원리 (0) | 2022.12.05 |
상속에서의 클래스 생성 (0) | 2022.12.05 |
상속과 다형성 (0) | 2022.12.05 |