DownCasting
class Car{
void drive(){
System.out.println("자동차가 움직인다");
}
}
class FireEngine extends Car {
void water() {
System.out.println("물을 쏜다");
}
void drive(){
System.out.println("소방차가 움직인다");
}
}
public class DownCasting {
public static void main(String args[]){
Car car = new Car();
FireEngine fe = new FireEngine();
car = fe; // Upcasting
car.drive();
FireEngine fe2 = new FireEngine();
fe2 = (FireEngine)car; // Downcasting
fe2.water();
}
}
출력결과:
소방차가 움직인다
물을 쏜다
car = ft , Upcasting 을 하는 줄에서 언뜻 이해하기에는 ft의 instance를 car에 넣었다고 생각할 수도 있다. 그러나 정확하게는, car의 참조변수가 참조하고 있는 참조값을 넣은것이다.
다음 그림은 Java의 정석 360page의 그림이다. ( 본 글과 메소드는 조금 다르다 )
Downcasting 뿐 아니라 Upcasting에서도 사실은 이와 같은 작용을 하는 것이다. 만약 Human extends Animal Class가 존재한다고 했을 때,
Animal animal = new Human() 이라고 했을 때, new 앞에 형 변환이 생략된 것이다.
Animal animal = (Animal)new Human()
형변환은 참조변수의 타입을 변환하는 것이지, 인스턴스를 반환하는 것은 아니기 때문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.
단지 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 것 뿐이다.
출처 : 자바의 정석 359page
DownCasting은 Upcasting해서 상위 클래스의 참조변수에 넣은 하위 클래스의 인스턴스를 다시 하위 클래스의 참조변수로 맞게 넣어주는 것을 의미한다.
이 때, Upcasting을 하지 않은 참조변수에 대해서 Downcasting을 하려고 하면 오류가 나는데, 위의 그림대로 생각해보면 이해하기 쉽다.
Car car2 = new Car();
FireTruck ft3 = null;
ft3 = (FireTruck)car2; // 오류 !!
ft3.water();
출력결과:
Exception in thread "main" java.lang.ClassCastException: class Car cannot be cast to class FireTruck (Car and FireTruck are in unnamed module of loader 'app')
at DownCasting.main(DownCasting.java:28)
// 컴파일 오류는 뜨지 않지만, 실행 시에는 에러가 뜬다
생성된 인스턴스는 Car 인스턴스인데, FireTruck이 어떤 값을 갖고 있는지 모르므로, JVM은 FireTruck의 범위를 어디까지 포함시켜야 할 지 모른다.
부모 클래스의 참조 변수가 자식의 인스턴스를 가르킬 때는 부모 클래스의 멤버값이 자식 클래스에 전부 포함되기 때문에 문제가 없지만, 반대의 경우에는 특정할 수 없으므로 문제가 된다.
조상타입의 인스턴스를 자손타입의 참조변수로 참조하는 것은 허용되지 않는다. 컴파일 시에는 참조변수간의 타입만 체크하기 때문에 실행 시 생성될 인스턴스의 타입에 대해서는 전혀 알지 못한다.
출처 : Java의 정석 361page
Instance Of
public class Test{
...
Animal humanAnimal = new Human();
Animal dogAnimal = new Dog();
Animal animal = new Animal();
Test test = new Test();
test.move(humanAnimal);
test.move(dogAnimal);
test.move(animal);
public void move(Animal animal){
animal.move();
}
다음과 같은 경우에서 move method는 매개변수로 Animal 클래스 혹은 그 자식의 인스턴스를 넘겨는다. 이 때 메서드 내에서 어떤 인스턴스를 받았는지 알 길이 없다. 이는 부모 타입은 자손 타입의 인스턴스를 참조할 수 있기 때문에, 참조변수의 타입과 인스턴스의 타입이 항상 일치하지는 않기 때문에 발생한다. 이런 문제에서, 어떤 인스턴스를 받았는지 알고 싶을 때 Instance of 를 사용한다.
( 매개변수가 잘못 넘어오는 것을 방지한다 )
Dog,human extends Animal
Animal humanAnimal = new Human()
Animal dogAnimal = new Dog()
Dog dog = (Dog)humanAnimal; // ERROR! 이런 실수가 나올 수 있다.
if(animal instance of Dog)
Dog dog = (dog)animal
public void move(Animal animal)
{
if(animal instance of dog)
...
}
instance of 는 true 혹은 false를 반환한다.
※ 부모와 자식 클래스에서, 같은 메소드 이름을 사용하는 경우 자식 클래스로 overriding돼서 자식의 메소드만 사용되지만, 멤버 변수에 있어서는 같은 이름이더라도 참조변수의 타입을 따라간다.
class Parent{
int x=10;
public void say(){
System.out.println("부모");
}
}
class Child extends Parent{
int x=20;
public void say(){
System.out.println("자식");
}
}
public class Test {
public static void main(String args[]){
Parent parent = new Child();
System.out.println(parent.x);
parent.say();
}
}
출력결과:
10
자식
Downcasting에 대해서 이렇게 이해할 수도 있다. Upcasting 이 일어난 후에만 Downcasting이 가능하다. 즉, 생성되는 것은 자식 클래스의 인스턴스이고, Upcasting 이후에는 무조건 자식의 인스턴스가 생성돼있다. 참조변수는 주소값만 가르키므로, 만약 Upcasting이 일어난 후가 아니라면, 자식의 인스턴스가 생성이 안돼있을 수도 있는 것이다. 부모의 인스턴스는 생성이 안돼있을 수도 있으나, 부모 인스턴스의 메소드나 멤버변수는 모두 자식메소드 안에 포함돼있으므로 상관없는 것이다.
'정리하기 이전 자료 ( before 20-11 ) > Java' 카테고리의 다른 글
인터페이스 (0) | 2020.03.30 |
---|---|
추상클래스 & 템플릿 메서드 (0) | 2020.03.30 |
상속에서의 생성자 (0) | 2020.03.29 |
다형성 , 상속, 가상함수 , 동적바인딩 (0) | 2020.03.28 |
ArrayList 기초 (0) | 2020.03.28 |
댓글