프로그래밍/java

[JAVA] 참조변수의 형변환

병뚜 2022. 8. 29. 23:09

참조변수의 형변환

기본형 변수와 같이, 참조변수 또한 형변환이 가능함.

조건: 서로 상속관계에 있는 클래스 사이에서만 가능하다.

- 자손타입 참조변수 → 조상타입 참조변수

- 조상타입 참조변수 → 자손타입 참조변수

 

class Car { }
class FireEngine extends Car { }
class Ambulance extends Car { }

위와 같이 클래스가 존재하는 경우, 

Car ↔ FireEngine

Car ↔ Ambulance

FireEngine (불가능) Ambulance

 

class Car { }
class FireEngine extends Car { }
class Ambulance extends Car { }

FireEngine f = new FireEngine();	// 인스턴스 = FireEngine
Car c = (Car)f;				// FireEngine 인스턴스를 Car로 형변환. 조상으로 형변환이기에 생략 가능
FireEngine f2 = (FireEngine)c;		// 본래 인스턴스가 FireEngine인 Car의 참조변수 c를 다시 FireEngine으로 형변환. 자손으로의 형변환이기에 생략 불가
Ambulance a = (Ambulance)f;		// FrieEngine의 참조변수 f를 Ambulance로 형변환 불가능. 에러
// f의 값(객체 주소)을 c에 저장
// 이 때, c와 f가 타입이 다르므로, 멤버 개수가 줄어드는 f는 자동 형변환(생략 가능)
Car c = f;
// 조상타입 c의 값(객체주소)를 자손타입 참조변수에 저장하려면.. 형변환 필요.
// 조상 타입을 자손으로 형변환하기에 생략 불가능.
f = (FireEngine) c;

참조변수의 형변환은 뭐 객체 이런게 바뀌지 않음 절대.

사용할 수 있는 "멤버"의 개수만 바뀐다.

 

 

여기서 내가 계속 헷갈리는데.. 책에 다음과 같이 적혀있다.

" 서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있으나, 참조변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다. 그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 먼저 확인하는 것이 중요하다 "

 

 

[내가 헷갈린 것]

- 조상에서 자손 타입으로의 형변환 시 타입 생략 불가

- 그런데 위에서는 참조변수가 가리키는 인스턴스의 자손 타입으로 형변환이 안된대! 뭐야?!?

 

[해결]

-  자손인 FireEngine으로 new 키워드 이용하여 객체 생성 (f 참조변수)

- 인스턴스가 FireEngine이기에, 조상인 Car로 형변환 가능(이때 생략 가능, 참조변수 c)

- Car로 형변환 하더라도.. 기능이 사라진 것이 아니라 기능을 사용할 리모컨 버튼이 하나 없어진 것

- 참조변수 c를 다시 f2로 형변환한다. 이 때, 자손 타입으로 형변환하는 것이기에 생략 불가능. FireEngine의 기능 다시 사용 가능해진다

▶ 결국 사용 가능한 멤버 개수 조절용일 뿐.. 헷갈리지 말자. 포인트는 인스턴스가 무엇인가? (new 위치 파악)

 

 

class Car {
	String color;
	int door;
	
	void drive() {
		System.out.println("drive, Brrrrr...");
	}
	void stop() {
		System.out.println("STOP!!");
	}
}

class FireEngine extends Car{
	void water() {
		System.out.println("warter!!!!");
	}
}

public class Ex7_7 {
	public static void main(String[] args) {
		Car car = null;
		FireEngine fe = new FireEngine(); // 인스턴스는 FireEngine임을 기억하
		FireEngine fe2 = null;
		
		fe.water();
		car = fe; // 생략 가능. 조상으로의 형변환
		//car.water(); 사용 불가능. car 리모컨은 water() 기능 버튼이 없다
		fe2 = (FireEngine)car; // 인스턴스가 FireEngine이기에 충분히 가능하다
		fe.water();
	}
}

결과

 

instanceof 연산자

- 참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위해 instanceof 연산자를 사용함.

- 주로 조건문에 사용.

- (참조변수) instanceof (타입, 클래스명)

- boolean값 반횐

void doWork(Car c) {
	if(c instanceof FireEngine) {
    		FireEngine fe = (FireEngine)c;
       		fe.water();
       	 	....
    }
}

위 함수는 Car 타입의 참조변수 c를 매개변수로 받는다.

즉, 매개변수로 Car 클래스 또는 그 자손 클래스의 인스턴스를 넘겨받는 것(다형성)

메서드 내에서는 정확히 어떤 타입의 인스턴스인지 알 수 없음.

이 때 instanceof 연산자로 c가 어떤 인스턴스를 가리키는지 확인한 후 적절히 형변환 후 작업한다.

 

조상 타입의 참조변수로 자손 타입의 인스턴스를 참조할 수 있기에.. 참조변수 타입과 인스턴스 타입이 항상 일치하지는 않음을 기억하자.

조상타입의 참조변수로는 실제 인스턴스 멤버들을 모두 사용할 수 없기 때문에... 실제 인스턴스와 같은 타입의 참조변수로 형변환 해야 인스턴스의 모든 멤버를 사용할 수 있음.

 

어떤 타입에 대한 instanceof 연산의 결과가 true라는 것은.. 검사한 타입으로 형변환이 가능하다는 의미이다.

// instanceof의 결과.. 조상들에 대해서도 참이 나온다는 사실을 기억하셈.
FireEngine fe = new FireEngine();
	fe instanceof Object 		//ture
    	fe instanceof Car		//ture
	fe instanceof FireEngine	//ture

- fe가 참조하는 인스턴스의 멤버 중 적어도 하나는 Object의 멤버이다.

(걍 모든 클래스의 root조상이 Object임)

▶ fe의 자식타입으로는 형변환 안되지만, 조상은 가능.^^