코린이의 소소한 공부노트

참조변수의 형변환 본문

Java

참조변수의 형변환

무지맘 2022. 4. 20. 01:16

참조변수의 형변환

  - 사용할 수 있는 멤버의 개수를 조절하는 것으로,

  - 조상-자손 관계의 참조변수는 서로 형변환이 가능하다.

class Parent { // 멤버 3개 }
class Child1 extends Parent { // 멤버 2개 }
class Child2 extends Parent { // 멤버 1개 }

// 메인 내부에서..
Child1 c1 = new Child1();

Parent 클래스를 상속받은 2개의 클래스 Child1, Child2가 있다. 메인에서 Child1 타입의 객체 c1을 생성 후, 어떨 때 참조변수의 형변환이 가능한지 확인해보자.

* 먼저 기억해내야 할 것: 범위가 작은 것 -> 큰 것은 캐스팅이 필요 없지만, 반대 상황에서는 필요하다

int i = 1;
double d = i; // OK
int ii = dd; // error. (int)dd로 바꿔야함

1) 자손->조상으로 형변환(up casting)

Parent p = (Parent)c1; // OK

  - 참조변수는 Parent

  - c1이 가리키는 객체는 Child1

  - 범위: p > c1 -> 조상-자손 관계이므로 형변환이 가능하다.

  - 접근 가능한 멤버 수: p 3개 < c1 5개 -> 멤버 수가 감소 -> 형변환을 생략할 수 있다.

  - p로 접근할 수 없는 멤버는 Child1에만 있는 멤버 2개다. -> 3개의 멤버는 사용 가능하다.

2) 조상->자손으로 형변환(down casting)

Child1 c2 = (Child1)p; // OK

  - 참조변수는 Child1

  - p가 가리키는 객체도 Child1

  - 범위: c2 < p -> 조상-자손 관계이므로 형변환이 가능하다.

  - 접근 가능한 멤버 수: c2 5개 > p 3개 -> 멤버 수가 증가 -> 형변환을 생략할 수 없다.

  - c2로 접근할 수 없는 멤버는 없다. -> 5개의 멤버 모두 사용 가능하다.

3) 상속관계가 아닌 클래스로의 형변환

Child2 c3 = (Child2)c1; // error

  - 참조변수는 Child2

  - c1이 가리키는 객체는 Child1

  - 범위: c3 ? c1 -> 조상-자손 관계가 아니므로 형변환이 불가능하다.

 

[형변환 정리]

1) 기본형의 형변환은 값이 바뀌는 것이다.

(int)3.14 -> 3
(double)3 -> 3.0

2) 참조형의 형변환은 사용(접근) 가능 멤버 개수만 바뀌는 것이다.

  - 형변환을 할 때 객체는 바뀌지 않는다.

  - 객체가 바뀌지 않기 때문에 주소 값도 바뀌지 않는다.

  - 참조변수를 형변환을 하면 형변환당한 참조변수(오른쪽)에 담겨있는 주소가 그대로 복사된다.

  - 참조변수의 타입에 따라 객체에 있는 멤버 중 사용 가능한 멤버가 무엇인지 그 개수만 바뀌는 것뿐이다.

글로만 보면 이해가 되지 않을 수 있으니, 위에서 예시로 든 코드와 다른 코드에 그림을 곁들여 설명해보겠다.

class Car { // 차
    String color; // 색상
    int door; // 문의 개수
    void drive() { } // 운전하기
    void stop() { } // 멈추기
}

class FireEngine extends Car { // 소방차
    void water() { } // 물뿌리기
}

  - Car라는 클래스에는 color, door라는 멤버 변수와 drive(), stop()이라는 메서드까지 총 4개의 멤버가 있다.

  - Car 클래스를 상속받은 FireEngine이라는 클래스는 water()라는 메서드와 상속받은 멤버까지 총 5개의 멤버가 있다.

이제 메인 메서드에서 형변환 실험을 해보겠다.

Car c = new Car();
FireEngine fe = (FireEngine)c;
fe.water();

  - Car 객체 c를 만들었다.

  - c를 자손인 FireEngine으로 형변환을 한 후 FireEngine 참조변수에 대입했다.

  -  fe에서 water() 메서드를 호출했다.

컴파일러는 코드를 확인해봤다.

  - Car가 FireEngine의 부모 클래스니까 형변환을 해야 하는 건 맞지!

  - fe는 자손의 참조변수니까 water()를 호출할 수 있겠구나. 오케이 통과!

하지만 에러가 발생했다.

FireEngine fe = (FireEngine)c; // runtime error. java.lang.ClassCastException

  - 컴파일러는 조상-자손 간의 형변환은 가능하니까 ok 해준 건데 실행시키니까 에러가 발생한 상황

  - 인스턴스(Car)의 멤버는 4갠데 참조변수(FireEngine)의 멤버는 5개라서 런타임 에러가 발생

  - 여기가 에러가 발생하지 않았다고 하더라도, fe.water()에서 런타임 에러가 발생

왜 에러가 나는지 그림으로도 확인해보자.

Car 객체를 생성하면 멤버가 4개다. 그런데 이 상태에서 형변환을 해서 자손 참조변수에 대입하게 되면, 자손 참조변수 fe는 어리둥절하다. 컴파일러가 통과시켜줬기 때문에 믿고 봤더니만, 난 5개의 멤버를 다룰 수 있는데 실제 객체에는 4개밖에 존재하지 않는다. 그래서 해당 코드에서 에러가 발생하는 것이다. 설사 여기를 통과했다 하더라도, 다음 줄에서 불러올 water()가 없기 때문에 어차피 또 에러가 발생한다.

 

참조변수의 형변환을 할 때

  - 참조변수가 실제로 가리키는 객체가 무엇인지 확인해야 한다.

  - 사용 가능한 멤버의 개수가 증가하지 않게 해야 한다.

'Java' 카테고리의 다른 글

다형성의 장점  (0) 2022.05.04
instanceof 연산자  (0) 2022.05.03
다형성  (0) 2022.04.19
접근 제어자를 이용한 캡슐화  (0) 2022.04.18
제어자  (0) 2022.04.16