CS Study 6주차: [Java] 캐스팅

hjern·2024년 3월 18일
0

CS Study

목록 보기
5/10

코드를 작성하다 보면 같은 타입 뿐만 아니라 서로 다른 타입 간의 연산이 필요할 수 있다. 이럴 땐, 연산 수행 전에 타입을 일치시켜야 하는데, 이 때 필요한 것이 형변환(casting)이다.

형변환

1. 형변환(Type Casting)

값은 타입을 다른 타입으로 변환하는 것이며, boolean 타입을 제외한 모든 기본형 변수는 서로 형변환이 가능하다.

2. 자동 형변환

서로 다른 타입 간의 대입, 연산을 할 땐 형변환을 통한 타입 일치가 필수적이지만 경우에 따라 형변환을 생략할 수 있다. 이 경우, 컴파일러가 자동으로 형변환을 해준다.

int i = 3;
double d = 1.0 + i;
// double d = 1.0 + (double)i; 와 동일하다.

기본적으로 자동 형변환이 되기 위해서는 아래의 규칙을 준수해야 한다.
byte > short > char > int > float > double

3. 강제 형변환

큰 크기 타입은 작은 타입으로 자동 형변환을 할 수 없다. 하지만 명시적으로 강제적인 타입 변환이 가능하다. 방법은 다음과 같다.

double d = 85.4;
int i = (int) d;
// int i = (int) 85.4;
// int i = 85;

위처럼 변수나 리터럴의 앞에 변환하고자 하는 타입을 괄호와 함께 작성하면 된다. 다만, 예처럼 큰 타입에서 작은 타입으로 변환할 때는 값손실(loss of data)가 발생할 수 있다는 단점이 있다.

4. 연산식에서의 자동 형변환과 강제 형변환

서로 다른 타입의 피연산자가 있을 때, 두 연산자 중 크기가 큰 타입으로 자동 형변환이 된 뒤, 연산을 수행한다.

int i = 10;
double d = 5.5;
result = i + d; // 15.5

int 타입으로 연산을 하고 싶다면, double을 int 타입을 강제 형변환한 후 연산을 수행할 수 있다.

int i = 10;
double d = 5.5;
result = i + (int)d; // 15

참조 변수의 형변환

1-1. 참조 변수의 형변환 (1)

기본형의 형변환은 값이 바뀌지만, 참조 변수의 형변환은 사용할 수 있는 멤버의 개수를 조절하는 것을 의미하며 주소값, 객체 등은 전혀 바뀌지 않는다.

또한, 기본적으로 조상과 자손의 관계일 때만 참조 변수의 형변환이 가능하다. FireEngine과 Ambulance는 같은 Car를 상속받았지만, 상호 간 형변환이 불가능하다.

class Car {
    String color;
    int door;

    void drive() {
        System.out.println("drive");
    }

    void stop () {
        System.out.println("stop");
    }
}

class FireEngine extends Car {

    void water() {
        System.out.println("water");
    }
}

class Ambulance extends Car {
}

FireEngine을 New 명령어를 통해 인스턴스를 생성한다면,

FireEngine fireEngine = new FireEngine();

아래 이미지와 같이 총 다섯 가지의 멤버를 활용할 수 있다. 한다면,

하지만 조상인 Car 타입으로 형변환하면

// 조상인 Car 타입으로 형변환 가능(생략 가능)
Car car = (Car) fireEngine;

아래 이미지와 같이 총 네 가지의 멤버를 활용할 수 있다(FireEngine이 가진 water() 메서드 사용 불가).

이번엔, Car 타입의 car를 FireEngine으로 형변환하여 fireEngine2 에 대입한다. 그러면 fireEngine2 역시 같은 주소값을 가리키게 되고, 이때 FireEngine 으로 형변환 했기 때문에 총 다섯 가지의 멤버 변수를 가질 수 있다.

// 자손인 FireEngine 타입으로 형변환 가능(생략 불가)
FireEngine fireEngine2 = (FireEngine) car;

앞서 설명한대로, 형제 간 상속은 불가하다.

// 상속 관계가 아닌 클래스 간의 형변환 불가
Ambulance a = (Ambulance) fireEngine; // 컴파일 에러

1-2. 예제

다음 예제를 통해, Car 타입은 FireEngine 형변환을 거쳐도 FireEngine 의 water() 메서드를 사용할 수 없음을 알 수 있다.

public static void main(String[] args) {
        Car car = null;
        FireEngine fe = new FireEngine();
        FireEngine fe2 = null;

        fe.water();
        car = fe; // car = (Car)fe; 에서 형변환 생략
        // car.water(); // 컴파일 에러
        fe2 = (FireEngine) car;
        fe2.water();
    }

조상 타입으로 형변환할 땐 일반적으로 멤버의 개수가 줄기 때문에, 항상 안전하며 따라서 생략할 수 있으나, 자손 타입으로 형변환할 때는 멤버의 개수가 늘어날 수 있기 때문에 상대적으로 불안전할 수 있고 따라서 형변환을 생략하지 못한다고 볼 수 있다.

2. 참조 변수의 형변환 (2)

여기서 주의해야할 점은, 참조 변수의 형변환이라고 해서 참조 변수의 타입만 따지는 것이 아니라, 참조 변수들이 실제 가리키는 인스턴스가 무엇인지 확인하고, 그 멤버의 개수를 넘어서서 사용할 수 없다는 사실을 인식하고 있어야 한다.

public static void main(String[] args) {
        // 컴파일상 형변환이 되지만 인스턴스 멤버를 소유하고 있지 않기 때문에 런타임 에러가 발생
        Car car = new Car(); // 객체가 Car 인스턴스 이기 때문에 water() 가 없다
        FireEngine fe = (FireEngine) car; // 형변환 런타임 에러 ClassCastException 발생
        // fe.water(); // 즉, 컴파일만 OK

    }

instanceof

참조 변수의 형변환 가능 여부를 확인하는데 사용하며, 가능하면 true를 반환한다. 항상 형변환해도 되는 지를 instanceof를 통해 확인하고, 그 다음에 형변환을 하도록 한다.

void doWork(Car c){ // new Car 와 동일
	if(c instanceof FireEngine) { // 형변환이 가능한 지 확인
    	FireEngine fe = (FireEngine) c; // 형변환
        fe.water;

상속 계층도에서 자기 자신을 포함해 조상들을 대상으로 한 instanceof()는 모두 참이 나온다.

FireEngine fe = new FireEngine();
System.out.println(fe instanceof Object); // true
System.out.println(fe instanceof Car); // true
System.out.println(fe instanceof FireEngine); // true

Object obj = (Object)fe; // OK
Car c = (Car)fe; // OK

예상 질문

Q: 형변환이란 무엇인가요?
✅ 기본형 데이터의 형변환은 값을 다른 데이터 유형으로 변환하는 것을 의미하지만 참조형 변수의 형변환은 값이나 주소값 등을 변환시키는 것이 아닌, 인스턴스의 사용할 수 있는 멤버 개수를 조절하는 것을 의미합니다.

Q: 자바에서 어떤 데이터 유형들은 자동 형변환이 가능한가요?
✅ boolean 타입은 제외하면 모든 기본형 데이터는 작은 데이터에서 큰 데이터(byte > short > char > int > float > double)로 이동해야 한다는 조건만 충족된다면 자동으로 형변환이 가능합니다.

Q: 강제 형변환은 언제 사용되나요?
✅ 큰 크기의 데이터 유형을 작은 크기의 데이터 유형으로 변환할 때 강제 형변환 즉, 명시적 형변환이 사용됩니다.

Q: 참조 변수의 형변환이란 무엇을 의미하나요?
✅ 참조 변수의 형변환은 사용할 수 있는 멤버의 개수를 조절하는 것입니다. 이는 주소값이나 객체는 변경되지 않고, 단순히 참조할 수 있는 멤버의 개수가 조정됩니다.

Q: 자손 클래스에서 조상 클래스로의 형변환과 반대로 형변환할 때 어떤 점을 주의해야 하나요?
✅ 자손 클래스에서 조상 클래스로의 형변환할 경우, 멤버의 개수가 줄어드는 게 일반적이라 항상 안전하여 생략할 수 있지만, 반대로 조상 클래스에서 자손 클래스로 형변환할 때는, 멤버의 개수가 늘어날 수 있으므로 불안전하여 생략할 수 없습니다.

Q: instanceof 연산자는 어떤 기능을 하나요?
✅ instanceof 연산자는 참조 변수의 형변환이 가능한지를 확인하는데 사용됩니다. 형변환이 가능하면 true를 반환합니다.

Q: 형제 클래스 간에도 형변환이 가능한가요?
✅ 아니요, 형제 클래스 간에는 형변환이 불가능합니다. 형변환은 상속 관계에서만 가능합니다.

참고자료
[Chapter 1 변수] 6. 형변환(Type Casting)
3. Java 자바 - 자동 타입 변환, 강제 타입 변환
[자바의 정석 - 기초편] ch7-24,25 참조변수의형변환(1)
[자바의 정석 - 기초편] ch7-24,25 참조변수의형변환(2)
[자바의 정석 - 기초편] ch7-26 instanceof 연산자

profile
주니어의 굴레는 언제 벗어날 것인가

0개의 댓글