코드를 작성하다 보면 같은 타입 뿐만 아니라 서로 다른 타입 간의 연산이 필요할 수 있다. 이럴 땐, 연산 수행 전에 타입을 일치시켜야 하는데, 이 때 필요한 것이 형변환(casting)이다.
값은 타입을 다른 타입으로 변환하는 것이며, boolean 타입을 제외한 모든 기본형 변수는 서로 형변환이 가능하다.
서로 다른 타입 간의 대입, 연산을 할 땐 형변환을 통한 타입 일치가 필수적이지만 경우에 따라 형변환을 생략할 수 있다. 이 경우, 컴파일러가 자동으로 형변환을 해준다.
int i = 3;
double d = 1.0 + i;
// double d = 1.0 + (double)i; 와 동일하다.
기본적으로 자동 형변환이 되기 위해서는 아래의 규칙을 준수해야 한다.
byte > short > char > int > float > double
큰 크기 타입은 작은 타입으로 자동 형변환을 할 수 없다. 하지만 명시적으로 강제적인 타입 변환이 가능하다. 방법은 다음과 같다.
double d = 85.4;
int i = (int) d;
// int i = (int) 85.4;
// int i = 85;
위처럼 변수나 리터럴의 앞에 변환하고자 하는 타입을 괄호와 함께 작성하면 된다. 다만, 예처럼 큰 타입에서 작은 타입으로 변환할 때는 값손실(loss of data)가 발생할 수 있다는 단점이 있다.
서로 다른 타입의 피연산자가 있을 때, 두 연산자 중 크기가 큰 타입으로 자동 형변환이 된 뒤, 연산을 수행한다.
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
기본형의 형변환은 값이 바뀌지만, 참조 변수의 형변환은 사용할 수 있는 멤버의 개수를 조절하는 것을 의미하며 주소값, 객체 등은 전혀 바뀌지 않는다.
또한, 기본적으로 조상과 자손의 관계일 때만 참조 변수의 형변환이 가능하다. 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; // 컴파일 에러
다음 예제를 통해, 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();
}
조상 타입으로 형변환할 땐 일반적으로 멤버의 개수가 줄기 때문에, 항상 안전하며 따라서 생략할 수 있으나, 자손 타입으로 형변환할 때는 멤버의 개수가 늘어날 수 있기 때문에 상대적으로 불안전할 수 있고 따라서 형변환을 생략하지 못한다고 볼 수 있다.
여기서 주의해야할 점은, 참조 변수의 형변환이라고 해서 참조 변수의 타입만 따지는 것이 아니라, 참조 변수들이 실제 가리키는 인스턴스가 무엇인지 확인하고, 그 멤버의 개수를 넘어서서 사용할 수 없다는 사실을 인식하고 있어야 한다.
public static void main(String[] args) {
// 컴파일상 형변환이 되지만 인스턴스 멤버를 소유하고 있지 않기 때문에 런타임 에러가 발생
Car car = new Car(); // 객체가 Car 인스턴스 이기 때문에 water() 가 없다
FireEngine fe = (FireEngine) car; // 형변환 런타임 에러 ClassCastException 발생
// fe.water(); // 즉, 컴파일만 OK
}
참조 변수의 형변환 가능 여부를 확인하는데 사용하며, 가능하면 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 연산자