Development/Java

(JAVA)참조변수 형변환과 instanceof 연산자

duckworth 2016. 3. 16. 14:39

#1.참조변수의 형변환

int,long,double,float 등.. 기본형 변수들은 형변환이 가능하다는 것을 배우

셨을겁니다. ex) (int)d   // double d = 30.0;

그렇다면 클래스가 타입에 오는 참조변수도 형변환이 될까요?

정답은 물론 됩니다.

하지만 서로 상속관계에 있어야만 가능하기때문에 자손타입의 참조변수를 조

상타입의 참조변수로,조상타입의 참조변수를 자손타입의 참조변수로만

형변환이 가능합니다.


이 특징을 응용해보면 모든 클래스의 상속관계를 거슬러올라가면 언젠가 

Object클래스가 모든 클래스의 조상이라는 것을 알 수가 있는데요. 

이 Object클래스 타입으로 모든 참조변수가 형변환이 가능합니다!

응용에서 벗어나서 다시 본론으로 돌아오면

기본형 변수의 형변환에서 작은 자료형에서 큰 자료형의 형변환은 생략이 가

능합니다.

ex) byte -> short -> int  -> long -> float -> double 

 왼쪽에서 오른쪽으로 변환하는 경우엔 형변환 생략이 됩니다.

하지만 오른쪽에서 왼쪽으로 변환하는 경우엔 형변환이 필요합니다.


참조형 변수의 경우엔 큰 자료형이 자손클래스고 작은 자료형이 부모클래스입

니다. 그렇다면 이렇게 적어볼수 있겠네요

ex) 자손타입 -> 조상타입 : 형변환 생략가능

자손타입 <- 조상타입 : 형변환 생략불가

영어로는 순서대로 UpCasting, DownCasting 이라 부른다고합니다.

참조변수끼리의 형변환 역시도 캐스트연산자를 사용하며, 기본형변수 변환하

듯이 '()'안에 넣어주시면됩니다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Car{
    String color;
    int door;
    void drive(){
        System.out.println("drive, Brrr~");
    }
    void stop(){
        System.out.println("stop!!!");
    }
}
 
class FireEngine extends Car{
    void water(){
        System.out.println("water!!!");
    }
}
 
class Ambulance extends Car{
    void siren(){
        System.out.println("siren~~~");
    }
}
 
cs

Car클래스는 FireEngine과 Ambulance의 조상입니다. 그런데 똑같은 클래스

를 상속받는 FireEngine클래스와 Ambulance클래스는 형제관계로 볼 수 있을

까요?

아쉽게도 그런 개념은 자바에 존재하지 않습니다.

오직 조상과 자손의 관계만 있습니다.

그리고 또한 FireEngine과 Ambulance는 조상,자손관계가 아니므로 형변환이

성립되지 않습니다만. FireEngine, Ambulance 둘다 부모인 Car로는 형변환

이 가능합니다.

ex)

1
2
3
4
5
6
7
8
Car c = new Car();
FireEngine f = new FireEngine();
Ambulance a = new Ambulance();
 
= (Ambulance)f;   // 컴파일 에러!
= (FireEngine)a;    // 컴파일 에러!
= f;
= a;
cs


이번에는 Car타입의 참조변수와 FireEngine타입의 참조변수 간의 형변환을 예로 들어보겠습니다.

1
2
3
4
5
6
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
 
car = fe;
fe2 = (FireEngine)car;
cs

참조변수 car와 fe 는 타입이 서로 다르기 때문에 연산을 수행하기전에 적절한 형변환을 수행해서 

참조변수 간의 타입을 맞춰주어야합니다.

앞에서 알게 된대로, 조상타입의 참조변수에 자손타입 참조변수를 할당할 경우에는 형변환을 생략할 수 있기때문

에 car = fe로 해도 오류가 나지 않으며 생략을 하지 않는다면 car = (Car)fe 로 쓰게 됩니다.

(*혹시나 car = (Car)fe 가 이전 강의의 다형성 개념 문법에 어긋난다 생각할수도 있는분들을 위해 적어보자면, 

형변환을 했을때에 오른쪽에 나오는 new FireEngine();인 인스턴스 참조하는것이 바뀌는 것이 아니라

그냥 타입만 바뀌어 Car fe = new FireEngine();이 되므로 문법에 어긋나지 않습니다.)

그러나, 반대로 자손타입의 참조변수에 조상타입의 참조변수를 할당할 경우에는 형변환을 생략할 수가 없으므

로, fe2 = (FireEngine)car; 와 같이 명시적으로 형변환을 해주셔야 합니다.



추가로 형변환을 생략할 수 있는 경우와 생략할 수 없는 경우에 대한 이유에 대해서 설명을 드리면

예를들어 Car타입의 참조변수 c가 참조하는 인스턴스는 Car인스턴스나 FireEngine인스턴스일 것입니다.

참조변수가 참조할수 있는 것은 인스턴스는 자식과 자신인데..

그렇다면 Car타입의 참조변수 c를 Car타입의 조상인 Object타입의 참조변수로 형변환하는 것은 참조변수가 다를

수 있는 멤버의 개수가 실제 인스턴스가 가지고 있는 멤버의 개수보다 적을 것이 분명하기 때문에 문제가 발생하지

않습니다. 그래서 형변환이 생략될 수 있는 것이지요.

그렇지만, Car타입의 참조변수 c를 자손인 FireEngine타입으로 변환하는것은 참조변수가 다룰 수 있는 멤버의 개

수를 늘리는 것이기 때문에, 실제 인스턴스의 멤버 개수보다 참조변수가 사용할 수 있는 멤버의 개수가 더 많아지

므로 문제가 발생할 가능성이 있습니다.

그래서 자손타입으로의 형변환은 생략할 수가 없으며, 형변환을 수행하기 전에 뒤에 길게 언급할 instanceof 연산

자를 사용해서 참조변수가 참조하고 있는 실제 인스턴스의 타입을 확인해주는 것이 안전합니다.


앞에서 설명했지만 거듭강조하면 형변환은 참조변수의 타입을 변환하는 것이지 인스턴스를 변환하는 것은 아니

기 문에 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않습니다.

다만 참조변수의 형변환을 통해서, 참조하고 있는 인스턴스에서 사용할 수 있는 멤버의 범위(개수)를 조절하는 

뿐입니다.

 ex) Tv t = new CaptionTv(); 은 Tv t = (Tv)new CaptionTv();의 생략된 형태입니다.

 또한, Tv t = (Tv)new CaptionTv();는 아래 두줄을 간략히 한 것입니다.                                  

1
2
3
CaptionTv c = new CaptionTv();
Tv t = (Tv)c;
 
cs