up-casting : supertype으로의 casting. 즉, child class type의 객체를 parent class type의 객체로 다룰 수 있게 하는 것.
down-casting : subtype으로의 casting. 즉, upcasting 된 객체를 다시 child class type의 객체로 다룰 수 있게 하는 것.
casting은 객체 자체를 바꾸는 것이 아니라 label을 다르게 붙이는 것 뿐이다. 예를 들어, Dog 객체를 만들고 이를 Animal 객체로 upcasting 했다고 해도 그 객체는 여전히 Dog 객체이다. 다만 그 객체는 이제 다른 Animal 객체와 같은 방식으로 다뤄지고 Dog property들은 숨겨지게 된다.
이렇게 하는 이유는, Animal 객체 group이 있을 때 어떤 동물이 meow()를 할지, bark()를 할지 알 수 없기 때문에 Animal 객체에서는 Child class의 property나 method에 접근할 수 없도록 하는 것이다.
upcasting은 항상 허용되는 반면, downcasting은 type check를 필요로 하고 ClassCastException을 throw 할 수 있다.
upcasting이 안전하다는 것은, 항상 Dog 객체를 Animal 객체로 casting 할 수 있고 Animal의 모든 property와 method를 사용 가능하다는 것을 의미한다.
downcasting에서 하는 것은, 프로그램에게 객체의 runtime type이 실제로는 무엇인지 말해주는 것이다. downcasting이 잠재적으로 안전하지 않은 이유는 child class의 method나 property 중 실제로 implement하지 않은 것을 사용하려고 할 수 있기 때문이다. 이는 아래 예시에서 확인할 수 있다.
Animal animal = new Animal();
Dog dog = animal;
이 코드는 compile time error를 발생시킨다. 이는 Parent class의 객체가 child class의 객체로 downcast 될 방법이 명시되어 있지 않기 때문이다.
Animal animal = new Animal();
Dog dog = (Dog) animal;
여기서는 compile time error가 발생하지는 않지만 runtime에 ClassCastException이 발생한다. animal의 runtime type이 Animal이기 때문에 runtime에 casting을 하려고 하면, animal은 Dog가 아니기 때문에(animal is not a Dog) ClassCastException이 발생한다.
Animal animal = new Dog();
Dog dog = animal;
여기서는 다시 compile time error가 발생한다. Animal을 Dog로 casting하는 방법을 명시해주지 않았기 때문이다.
Animal animal = new Dog();
Dog dog = (Dog) animal;
이 코드가 제대로 downcasting을 하는 방법이다.
public class Animal {
public void walk()
{
System.out.println("Walking Animal");
}
}
class Dog extends Animal {
public void walk()
{
System.out.println("Walking Dog");
}
public void sleep()
{
System.out.println("Sleeping Dog");
}
}
class Demo {
public static void main (String [] args) {
Animal a = new Animal();
Dog d = new Dog();
a.walk();
d.walk();
d.sleep();
//upcasting
Animal a2 = (Animal)d;
a2.walk();
//a2.sleep(); error
//downcasting
Animal a3 = new Dog();
//Dog d2 = a3; //compile time error
Dog d2 = (Dog)a3;
d2.walk();
d2.sleep();
//Run time error: Animal cannot be cast to Dog
Animal a4 = new Animal();
//Dog d3 = (Dog)a4;
//d3.walk();
//d3.sleep();
}
}
Walking Animal
Walking Dog
Sleeping Dog
Walking Dog
Walking Dog
Sleeping Dog
upcasting을 사용하는 이유는 다형성 때문이다. 어떤 객체의 정확한 runtime type을 알 수 없는 경우에, 이를 이용할 수 있다. 이는 Parent 참조 변수가 어떤 child class 객체든 가지고 있을 수 있기 때문이다.
class Parent {
void work(){
System.out.println("working");
}
}
class Child1 extends Parent{
void relax(){
System.out.println("relaxing 1");
}
}
class Child2 extends Parent{
void relax(){
System.out.println("relaxing 2");
}
}
class Child3 extends Parent{
void relax(){
System.out.println("relaxing 3");
}
}
class Child4 extends Parent{
void relax(){
System.out.println("relaxing 4");
}
}
public class Demo {
public static void main (String [] args) {
List<Parent> lst = new ArrayList<Parent>();
lst.add(new Child1());
lst.add(new Child2());
lst.add(new Child3());
lst.add(new Child4());
for(Parent p:lst){
p.work();
}
Child1 c1 = (Child1)lst.get(0);
c1.relax();
Parent parent = getChild("2");
parent.work();
}
public static Parent getChild(String childName){
Parent p = null;
if(childName.equals("1")){
p = new Child1();
} else if (childName.equals("2")){
p = new Child2();
} .......
return p;
}
}
working
working
working
working
relaxing 1
working