
4. 참조 자료형의 형 변환다음과 같은 상속 관계와 객체 생성 방식을 보자.
class SupClass {}
class SubClass extends SupClass {}
SupClass test1 = new SubClass(); // available
SubClass test2 = new SupClass(); // not available | compile error
error: incompatible types: SupClass cannot be converted to SubClass
SubClass test2 = new SupClass();
^
1 error
이를 통해 참조형 간 타입 캐스팅에 대해 생각할 수 있다.
(Sub --> Sup | Permitted)(Sup --> Sub | Conditional)잘 생각해보면 이는 매우 합당한 논리이다.
(자식 --> 부모 | 가능)(부모 --> 자식 | 조건부)때문에 (자식 --> 부모) 캐스팅은 허용되는 반면, (부모 --> 자식) 는 가능할 수도 있고 아닐 수도 있는 것이다.
그런데 그럼 왜 (자식 --> 부모) 캐스팅이 될수도 있고 아닐 수도 있냐 되물을 수 있다.
이는 "Java 가 결국 중요시 하는 것은, 객체를 가르키고 있는 그것의 실체" 이기 때문이다.
다음 예시를 보자.
class SupClass {}
class SubClass extends SupClass {}
SubClass test_sub = new SubClass();
SupClass sub_ref = new SubClass(); // Sub --> Sup : Permitted
SupClass test1 = new SubClass(); // Sub --> Sup : Permitted
SupClass test2 = (SupClass) new SubClass(); // Sub --> Sup : Permitted
SupClass test3 = (SupClass) test_sub; // Sub --> Sup : Permitted
Subclass test4 = new SupClass(); // compile-time error
SubClass test5 = (SubClass) new SupClass(); // runtime error
SubClass test6 = (SubClass) sub_ref; // No error occurs
먼저 test4 와 test5 를 비교해보자. test4 의 경우 이전 예시에서 에러를 뱉어내는 것을 확인하였다.
하지만 test5 의 경우 놀랍게도 소스코드가 컴파일 가능하고, 실행시켜야 에러를 뱉어낸다.
test4 와 test5 는 무엇이 다른가? 바로 타입 캐스팅을 명시해 준 점 이다.
Java 에서 타입 캐스팅을 명시적으로 적는 것은 다음처럼 비유할 수 있다. [1]
"날 믿어. 분명 적혀있는 건 SupClass 이지만 실행시켜서 보면 분명히 SubClass 일 거야. 그러니까 안심하고 캐스팅 해도 되."Java 는 이를 믿고 컴파일까지 진행했다. 그런데 직접 실행해서 확인해보니 "방금 말한 약속" 이 틀렸던 것이다. 때문에 Java 는 눈물을 머금고 runtime error 를 뱉어낸 것이다.
이를 test6 에도 적용하면 꽤나 명료해 진다.
test6 부분에도 명시적 타입 캐스팅을 이용했다. 때문에 Java 는 "지금 눈에 보이는 건 SupClass (sub_ref 의 타입) 이지만, 분명 실행하다 보면 SubClass 일거야!" 라고 생각한다.
그런데 sub_ref 의 정의를 보면 SubClass 객체가 SupClass 타입으로 캐스팅 된 것을 볼 수 있다. 즉 sub_ref 는 SupClass 타입 이지만, 직접 가르키고 있는 것은 SubClass 인 객체인 것이다.
때문에 Java 는 test6 을 실행시키며 어떠한 에러를 뱉어내지 않는 것이다.
이런 방식을 왜 사용하는 것일까? 당연히 이점이 있으니 사용한다.
class SupClass {
public void common_method()
}
class SubClass extends SupClass {
public void child_method() {}
}
SupClass[] object_array = new SupClass[3];
SubClass obj1 = new SubClass();
SupClass sup_object = new SupClass();
SubClass obj3 = new SubClass();
object_array[0] = obj1;
object_array[1] = sup_object;
object_array[2] = obj3;
for (Object obj : object_array)
{
if (obj instanceof SubClass)
{
System.out.println("SubClass elemnt");
((SubClass) obj).child_method();
}
else if (obj instanceof SupClass)
{
System.out.println("SupClass elemnt");
((SupClass) obj).common_method();
}
else
{
System.out.println("Unknown element");
}
}
SubClass elemnt
SupClass elemnt
SubClass elemnt
위 처럼 상속 관계와 캐스팅을 이용하면 뭔가 할 수 있는 것이 많아진다.
5. PolymorphismPolymorphism 의 영문 뜻은 다향성 이다. Java 에서 다향성 은 "하나의 객체가 여러 타입을 지칭할 수 있는 성질" 을 말한다. [2]
사실 이는 이미 앞선 예시에서 확인하였다. Java 는 부모 클래스 타입의 참조 변수로 자식 클래스 타입의 인스턴스를 참조할 수 있도록 하는 등의 다향성 이 구현되어 있다.
SubClass sub = new SubClass();
SupClass sup = (SupClass) sub; // Polymorphism example
우리가 앞서 보았던 예시는 "서브타입 다향성" (Subtype polymorphism / Inclusion polymorphism / Subtyping) 으로, 이 외에도 "매개변수의 다향성" (Parametric polymorphism), "임시 다향성" (Ad Hoc polymorphsim) 등의 다향성이 존재한다.
Explicit casting from super-class to sub-class[1] : By using a cast you're essentially telling the compiler "trust me. I'm a professional, I know what I'm doing and I know that although you can't guarantee it, I'm telling you that this animal variable is definitely going to be a dog."Polymorphism (computer science) - Wikepedia[2] : In programming language theory and type theory, polymorphism is the use of a single symbol to represent multiple different types.