상속으로 알아보는 객체 형변환(TypeCasting)과 사용
1. 형변환(TypeCasting)
변수의 형변환은 변환하고자하는 변수앞에 (괄호)를 넣고, 괄호안에 자료형을 넣어 대입연산자 = 을 통해 이루어진다.
int j = 10;
short k = (short)j; // 큰 용량의 데이터를 작은 용량에 넣는 것은 데이터 손실
자료형의 크기에 따라 작은 용량의 데이터를 큰 용량에 대입연산자만을 통해 넣기도 한다.
short s = 10;
int i = s; // 작은 용량의 데이터를 큰 용량에 넣는 것은 문제 없음
이렇듯 강제형변환을 통해 모든 데이터들을 형변환할 수 있을 것 같지만, 서로 다른 클래스 객체간의 형변환은 허용되지 않는다.
class NormalA{
int num;
}
class NormalB{
int num;
}
NormalA a2 = (NormalA)b1; // 에러
NormalB b2 = (NormalB)a1; // 에러
NormalB b3 = new NormalA(); // 에러
NormalA a3 = new NormalB(); // 에러
위와 같은 방식으로는 형변환 시도 자체가 안된다.
2. 객체간의 TypeCasting _ 객체 선언
그렇다면 객체간의 형변환은 영영 시도할 수 없는 것일까?
꼭 그렇지만은 않다.
class SuperF{ }
class SubF extends SuperF{ }
위와 같은 SuperF 클래스와 SuperF클래스를 상속하는 SubF 클래스가 있다고 하자.
먼저, 당연하게도 자기 자신의 인스턴스와 참조변수로 만드는 객체는 자유롭게 생성이 가능하다.
예시에 사용될 예정이니 객체 또한 각각 만들어두겠다.
// 자기 자신의 인스턴스와 참조변수로 만드는 객체는 자유롭게 생성 가능
SuperF f1 = new SuperF();
SubF f2 = new SubF();
방법1. 상위클래스의 참조변수에 하위클래스의 인스턴스 주소를 저장
SuperF super1 = f2;
SuperF super2 = new SubF();
상위클래스 참조변수인 super1과 super2를 각각 선언하고, 이에 하위클래스인 SubF 객체를 대입하거나, new 연산자를 통해 하위클래스를 생성할 수 있다.
SubF sub1 = f1;
SubF sub2 = new SuperF();
반대로 하위클래스의 참조변수에 상위클래스의 인스턴스 주소는 저장할 수 없다.
SuperF super3 = new SuperF();
SubF sub3 = (SubF)super3; // 런타임 에러
강제캐스팅(SubF)을 통해 문법상의 오류는 없앨 수 있지만 실행하면 런타임 오류가 발생한다.
방법2. 상위클래스 참조변수가 하위클래스의 인스턴스 주소를 갖고 있는 경우, 상위클래스 참조변수에 지정된 값을 하위클래스 참조변수에 옮겨 담을 수 있다.
SuperF super4 = new SubF();
SubF sub4 = (SubF)super4; // sub4에 저장되는 건 하위클래스의 인스턴스 주소이기 때문에 가능
무슨 말인가 함은... 방법1에서처럼 상위클래스 참조변수(super4)가 하위클래스의 인스턴스 주소(new SubF())를 저장한 경우, 하위클래스 참조변수(sub4)에 강제형변환을 통해 '옮겨' 담을 수 있다. 는 말이다. 전제조건이 있는 셈.
결국 자식 인스턴스 주소를 자식 참조변수(주소를 담는 변수)에 담는 건 가능하지만 부모는 안 된다는 말이다.
정리하자면...
자식(하위) 인스턴스의 주소 → 부모(상위) 참조변수 → (타입캐스팅) → 자식(하위) 참조변수 : OK
부모(상위) 인스턴스의 주소 → 부모(상위) 참조변수 → (타입캐스팅) → 자식(하위) 참조변수 : NOT OK (런타임에러)
해당 방법은 '강제' 형변환이기 때문에 자료형을 신경써야 한다.
만약 타입캐스팅 연산에서 상위-하위간의 자료형으로 매칭되지 않으면 에러가 발생한다.
당연하게도, 타입캐스팅하려는 상위클래스 참조변수에 저장된 값이 하위클래스 인스턴스 주소가 아니어도 에러가 발생한다.
조금 더 확실하게 런타임 에러없이 타입캐스트를 진행하고자 한다면, ' 하위클래스 참조변수 <- (타입캐스팅) 상위클래스 참조변수 ' 관계가 가능한 관계인지 검사하면 된다.
SuperF super7 = new SubF();
SubF sub7;
// super7이 저장한 값은 SubF형으로 타입캐스팅이 가능한가?
if(super7 instanceof SubF) {
sub7 = (SubF)super7;
System.out.println("super7 형변환 성공!");
}else {
System.out.println("super7 형변환 실패!");
}
instanceof 연산자를 이용하면 객체가 클래스의 속하는지 여부와 객체의 상속관계를 알 수 있다.
직역하면 super7은 SubF의 인스턴스 객체인가? 의 뜻으로, 위와같이 if문을 통해 코드를 작성한다면 상위클래스 객체인 super7이 하위클래스인 SubF의 인스턴스 주소값을 갖고 있어야만 형변환이 이루어지게 된다.
SuperF super8 = new SuperF(); // 부모 인스턴스가 들어있는 super8은 실패
SubF sub8;
// super8이 저장한 값은 SubF형으로 타입캐스팅이 가능한가?
if(super8 instanceof SubF) {
sub8 = (SubF)super8;
System.out.println("super8 형변환 성공!");
}else {
System.out.println("super8 형변환 실패!");
}
3. 객체간의 TypeCasting _ 사용
만약 이러한 방법들로 타입캐스팅이 이루어진 경우, 경우에 따라 클래스 안에 있는 변수와 메소드를 자유롭게 사용하지는 못한다.
class SuperF{
int superNum;
void superMethod() {
System.out.println("SuperMethod 호출");
}
}
class SubF extends SuperF{
int subNum;
void superMethod() {
System.out.println("SuperMethod 호출");
}
}
해당 클래스를 이용해 알아보자.
SuperF super5 = new SubF();
super5.superNum = 100;
super5.superMethod();
// super5.subNum = 200; // 에러
// super5.subMethod(); // 에러
예제의 super5 는 하위클래스(SubF)의 인스턴스 주소를 담았다. 이 경우 super5는 상위클래스인 SuperF에서 물려준 멤버만 접근을 할 수 있다. 하위클래스에서 새롭게 만들어진 subNum, subMethod()는 사용할 수 없는 것이다.
SubF sub5 = (SubF)super5;
sub5.subNum = 300;
sub5.subMethod();
sub5.superNum = 100;
sub5.superMethod();
하위클래스를 참조하는 super5를 하위클래스 객체 sub5에 강제형변환을 통해 대입했다. 이 경우는 상위하위 할 것 없이 모든 클래스를 이용가능하다.
오버라이딩과도 재미난 관계가 있는데...
class SuperF{
int superNum;
void superMethod() {
System.out.println("SuperMethod 호출");
}
}
class SubF extends SuperF{
int subNum;
void subMethod() {
System.out.println("SubMethod 호출");
}
// 상위 클래스의 메소드를 오버라이딩
void superMethod() {
System.out.println("오버라이딩 된 SuperMethod 호출");
}
}
SubF 클래스에 상위클래스 메소드를 오버라이딩한 superMethod()를 추가했다.
SuperF super6 = new SubF();
super6.superMethod(); // 오버라이딩의 경우 접근 가능
하위클래스 인스턴스 주소를 저장한 상위클래스 객체 super6는 앞서 다루었던 것처럼 하위클래스의 메소드를 실행할 수 없다. 그러나 하위클래스에서 상위클래스의 메소드를 오버라이딩한 경우는 접근이 가능하다. (와!)
상위클래스 참조변수로 상위클래스 메소드를 호출했는데 하위클래스의 오버라이딩된 메소드가 우선 실행되는 것이다.
'Backend > Java' 카테고리의 다른 글
[JAVA] Collection 클래스와 Vector, ArrayList (0) | 2024.10.17 |
---|---|
[JAVA] Object 클래스 속 toString()과 equals() 오버라이딩 (0) | 2024.10.16 |
[JAVA] 상속(extends)과 super, 오버라이딩(overriding) (0) | 2024.10.15 |
[JAVA] 싱글톤 패턴(Singleton Pattern)과 초기화 블록 (0) | 2024.10.14 |
[JAVA] static과 인스턴스(instance) (0) | 2024.10.14 |