관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch7 - 24 ~ 26[참조변수의형변환,instanceof 연산자] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch7 - 24 ~ 26[참조변수의형변환,instanceof 연산자]

소소한나구리 2023. 11. 27. 14:00

1) 참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절 하는 것
  • 기본형의 형변환은 값이 바뀌지만 참조변수(참조형)의 형변환은 값(참조값, 객체)이 바뀌는 것이 아님 - 중요
  • 단지 객체를 참조하는 방식만 변경되는 것일 뿐임
  • 상속관계와 구현관계(인터페이스를 구현한 클래스)에서 사용 가능

 

(1) 예제1

  • FireEngine 객체를 생성하여 참조한 참조변수를 업캐스팅, 다운캐스팅 -> 모두 성공적으로 적용됨
  • 업캐스팅: 자식 클래스의 객체를 부모 클래스의 타입으로 변환하는 것 -> 더 일반적인 타입으로 변환, 형변환 타입 생략 가능
  • 다운캐스팅: 부모 클래스의 타입으로 참조된 자식 클래스의 객체를 자식 클래스의 타입으로 변환하는 것 -> 구체적인 타입으로 변환, 형변환 타입 생략 불가능
  • 업캐스팅, 다운캐스팅을해도 참조값이 바뀌는 것이 아니라는 점

** 주의

  • 애초에 부모 클래스 타입의 객체를 자손타입으로 형변환하거나, 부모클래스 타입의 객체를 참조하는 참조변수를 다운캐스팅하면 컴파일에서는 에러가 안나지만 실제 프로그램을 실행하면 ClassCastException 에러가 발생함 - 예제2 상황
class Ambulance { }

class Car {	// 멤버가 4개인 Car클래스
	String color;
	int door;
	
	void drive () {
		System.out.println("부릉부릉");
	}
	void stop () {
		System.out.println("끼이익");
	}
}

class FireEngine extends Car {	// 멤버가 5개인 FireEngine클래스(Car멤버 상속)	
	void water() {
		System.out.println("water");
	}
}


public class ex07_write2 {
	public static void main(String srg[]) {
		FireEngine f = new FireEngine(); // FireEngine 인스턴스 생성
		
		// 업캐스팅: 자손타입의 참조변수를 조상의 타입으로 형변환 시에는 형변환타입을 생략 가능함
		// 조상 클래스의 멤버만 사용 가능, water()사용 불가
		Car c = f;	// 참조변수 f를 Car타입의 c 참조변수로 형변환
		
		// 다운캐스팅: 조상 타입의 참조변수지만 참조한 객체는 자손타입의 객체이므로 다운캐스팅이 가능
		// 모든 클래스의 멤버 사용 가능
		FireEngine f2 = (FireEngine)c;
		
		// Ambulance a = (Ambulance)f;	// 실행시 에러! 상속관계가 아닌 클래스간의 형변환은 불가능,
	}
}

 

(2) 예제2 - 다운 캐스팅시 주의점

  • 자손타입의 참조변수는 조상타입의 객체를 가르킬 수 없음 (형변환 에러 발생)
  • 조상클래스 객체를 참조한 조상타입 참조변수를 자손클래스 타입으로 형변환(다운캐스팅)을 하면 컴파일에러는 나지않지만 실행하면 ClassCastException 에러가 발생(조상 클래스의 객체를 생성할 때 다운캐스팅해도 동일하게 에러남)
  • 즉, 참조변수의 형변환이나 객체의 형변환 시 중요한 점은 참조변수가 가르키고있는 객체가 무엇인지가 중요하며 해당 객체가 가진 멤버의 집합이 참조변수의 타입이 가지고 있는 멤버의 집합을 모두 포함해야 함
  • 형변환 시 타입 생략 여부를 고민하는 것은 그 이후에 문제임
class Car2 {
	String color;
	int door;
	
	void drive () {
		System.out.println("부릉부릉");
	}
	void stop () {
		System.out.println("끼이익");
	}
}

class FireEngine2 extends Car2 {
	void water() {
		System.out.println("water");
	}
}

public class ex7_7 {
	public static void main(String srg[]) {
    
    	// 객체 생성없이 참조변수를 null로 초기화만 하고 실행하면 NullPointerException 에러가 발생
		Car2 car2 = null;	    // 참조변수를 null로 초기화
		FireEngine2 fe2 = null;	
		
		fe2 = new FireEngine2();	// 자손 타입의 인스턴스 생성	
		
		fe2.water();	// 참조변수 f2e가 가리키는 객체의 water메서드 호출 -> OK
		car2 = fe2;	// 조상(Car2) 타입으로 형변환(형변환 생략 가능)
		
//		car2.water(); // 컴파일에러, Car2타입의 참조변수는 water()를 호출 불가능
		car2.drive(); // 참조변수 car2가 가리키는 객체의 drive메서드 호출 -> OK
		
		fe2 = (FireEngine2)car2; // 다시 자손타입으로 형변환(형변환 생략 불가)
		fe2.water();	// 참조변수 fe2가 가리키는 객체의 water메서드 호출 -> OK
		
//		형변환 에러 - 주의	
//		조상타입인 실제 객체를 자손타입으로 형변환 후 실행하면 java.lang.ClassCastException 에러 발생
		Car2 c2 = new Car2();
		FireEngine2 f2 = (FireEngine2)c2;	// 자손타입의 참조변수로 조상 객체를 가르킬 수 없음
		f2.water(); // 컴파일에서는 문제없지만 실행시 에러 발생
        
		// 자손타입의 참조변수로 조상의 객체를 참조할 수 없음
		FireEngine2 f2 = new Car2(); // 컴파일 에러 발생
	}
}

3) instanceof 연산자

  • 참조변수의 형변환 가능여부 확인에 사용 -> 가능하면 true반환
  • 다운캐스팅 에러 방지를 위해 형변환 전에 반드시 instanceof로 확인 후 형변환 하는 것을 권장함, 관례적으로 사용됨
FireEngine fe = new FireEngine();
System.out.printl(fe instanceof Object); // True (최고 조상으로 형변환 가능)
System.out.printl(fe instanceof Car);	 // True (조상으로 형변환 가능)
System.out.printl(fe instanceof FireEngine);	// True


// 형변환 가능 여부를 메서드 형태로 만들어서 필요할때마다 호출하여 확인해도 좋음
// doWork()에 참조변수를 입력하면 형변환 가능 여부를 판단해서 true이면 로직을 수행함
static void doWork(Car c) {
    if (c instanceof FireEngine) {          // FireEngine 클래스타입으로 형변환이 가능한지 확인
        FireEngine3 fe = (FireEngine) c;    // water()메소드를 호출하기 위해 형변환
        fe.water();
    }
    // 기타 코드들 ...

4) QnA

(1) Q. 참조변수의 형변환을 하는 이유?

  • A. 사용할 수 있는 멤버의 갯수를 조절하기 위해서

(2) Q. instanceof연산자는 언제 사용?

  • A. 참조변수를 형변환하기 전에 형변환 가능여부를 확인할 때, 객체의 타입을 확인할 때 사용

 

 

** 출처 : 남궁성의 정석코딩_자바의정석