HOWTO · Java
Java의 java.lang.ClassCastException
이 문서는 Java에서 java.lang.ClassCastException을 처리하는 데 도움이 됩니다.
이 페이지의 내용
java.lang.ClassCastException은 객체의 런타임 형식이 캐스트 대상 형식과 호환되지 않을 때 발생합니다. 해결하려면 실제 객체 형식과 캐스트 대상을 비교한 다음 instanceof로 확인하거나, 형식이 지정된 제네릭을 사용하거나, 설계를 바꾸어 캐스트가 유효하도록 하세요. 프로그램에서 유용한 복구 작업을 할 수 있을 때만 이 예외를 잡으세요.
ClassCastException이란?
Java에서는 런타임에 유효할 가능성이 있는 관계의 형식끼리 캐스트할 수 있습니다. 캐스트는 객체 자체를 바꾸지 않습니다. 런타임에 Java는 해당 객체가 대상 클래스의 인스턴스인지, 또는 대상 인터페이스를 구현하는지 확인합니다. 그렇지 않으면 java.lang.ClassCastException을 던집니다.
예를 들어 Dog와 Cat이 모두 Animal을 상속한다면 Animal 참조는 어느 쪽 객체든 가리킬 수 있습니다. Animal 참조를 Dog로 캐스트하는 것은 그 참조가 실제 Dog 객체를 가리킬 때만 유효합니다. Cat 객체를 형제 형식인 Dog로 캐스트할 수는 없습니다. 반면 Dog를 Animal 참조에 할당하는 것은 업캐스트이며, 모든 Dog는 Animal이므로 안전합니다.
Java에서 ClassCastException이 발생하는 이유
흔한 원인은 넓은 형식으로 선언된 참조가 특정 하위 클래스의 객체를 담고 있다고 가정하는 것입니다. 컴파일러는 다운캐스트가 구조적으로 가능한지 확인할 수 있지만, 런타임에 그 참조가 어떤 객체를 가리킬지 항상 알 수는 없습니다. 런타임 검사가 이 불일치를 찾아냅니다.
코드가 Object 형식으로 값을 받거나, 제네릭이 없는 레거시 컬렉션을 사용하거나, API가 반환하는 구체적인 클래스에 대해 가정할 때 이런 문제가 자주 발생합니다. 다형성 자체는 문제가 아닙니다. 부모 클래스 참조를 통해 오버라이드된 메서드를 호출하는 것은 안전합니다. 객체에 실제로 없는 하위 형식으로 참조를 좁히려 할 때 실패합니다.
로 타입 컬렉션과 확인되지 않은 형 변환은 제네릭의 형식 검사를 우회할 수도 있습니다. 값을 나중에 가져올 때 암묵적인 캐스트가 삽입되어 값을 사용하는 시점에 실패할 수 있습니다. 값이 컬렉션에 들어온 위치와 그 형식에 대한 가정이 생긴 위치를 추적하세요. 예외가 발생한 줄만 바꾸면 불일치의 원인을 가릴 수 있습니다.
ClassCastException 예방 방법
참조와 컬렉션은 용도에 맞는 범위에서 가능한 한 구체적인 형식으로 선언하세요. 제네릭을 사용하면 컴파일러가 컬렉션에 값을 넣고 가져오는 과정을 검사하므로 캐스트의 필요성이 줄어듭니다. 그렇다고 임의의 객체를 명시적으로 캐스트해도 안전해지는 것은 아닙니다. 로 타입이나 확인되지 않은 연산은 제네릭의 보호를 약화할 수 있습니다.
원본 형식과 대상 형식이 관련되어 있으면 다운캐스트가 컴파일될 수 있지만, 객체가 실제로는 형제 하위 형식일 경우 실패합니다. 다음 예제는 이 실패를 잡아 프로그램이 정상적으로 끝나도록 합니다. 예제의 코드와 출력은 바이트 단위로 보존되므로 영어 문자열과 출력 내용도 영어로 표시됩니다.
이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.
public class FailingCastExample {
static class Animal {}
static class Dog extends Animal {}
static class Cat extends Animal {}
public static void main(String[] args) {
Animal animal = new Cat();
try {
Dog dog = (Dog) animal;
System.out.println(dog);
} catch (ClassCastException exception) {
System.out.println(exception.getClass().getName());
}
}
}
java.lang.ClassCastException
catch 블록이 받는 예외는 java.lang.ClassCastException입니다. 객체는 Dog가 아니라 Cat입니다. JVM이 생성하는 메시지와 스택 추적은 런타임 환경에 따라 달라질 수 있으므로, 예외 형식과 캐스트가 발생한 줄을 확인해 불일치를 진단하세요. 하위 형식에만 해당하는 동작이 필요하지 않다면 값을 Animal 형식으로 유지하세요.
값을 의도적으로 넓은 형식으로 다룰 때는 형식을 좁히기 전에 런타임 형식을 확인하세요. 다음은 안전한 캐스트를 보여 주는 독립적인 예제입니다. 예제의 코드와 출력은 바이트 단위로 보존되므로 영어 문자열과 출력 내용도 영어로 표시됩니다.
public class CastExample {
static class Animal {
String name() { return "Milo"; }
}
static class Dog extends Animal {
String bark() { return "woof"; }
}
public static void main(String[] args) {
Animal animal = new Dog();
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
System.out.println(dog.bark());
}
}
}
instanceof로 확인하므로 객체가 Dog일 때만 캐스트가 실행됩니다. 하위 형식에만 해당하는 동작이 필요하지 않다면 값을 Animal 형식으로 유지하고 공통 API를 사용하세요. instanceof 패턴 매칭을 지원하는 Java 버전에서는 형식 확인과 변수 바인딩을 함께 할 수 있습니다. 이 예제는 폭넓은 호환성을 위해 기존 문법을 사용합니다.
ClassCastException 처리 및 진단 방법
예외 메시지와 스택 추적을 읽어 원본 형식, 요청된 대상 형식, 캐스트가 발생한 줄을 확인하세요. 그런 다음 반환된 객체, 컬렉션의 내용, 역직렬화 매핑 또는 API 계약을 살펴보며 값의 출처를 추적하세요. 잘못된 가정이 생기는 지점에서 형식 검사를 추가하거나 선언된 데이터 모델을 바로잡으세요.
실패한 캐스트가 예상 가능한 입력 조건이고 프로그램이 복구할 수 있다면 try-catch 블록을 사용하는 것이 적절합니다. 예를 들어 여러 레코드를 처리하면서 형식이 잘못된 레코드 하나를 거부할 수 있습니다. 일반적인 애플리케이션 로직에서 ClassCastException을 잡은 뒤 잘못된 가정을 바로잡지 않고 계속 실행하면 데이터가 누락되거나 버그가 감춰질 수 있습니다. try 블록의 범위를 좁게 유지하고 문제 값을 진단할 수 있도록 충분한 맥락을 기록하세요. Java 구현과 런타임 버전에 따라 예외 메시지 표현이 달라질 수 있으므로 여기에는 메시지를 제시하지 않습니다.
자주 묻는 질문
java.lang.ClassCastException이란?
대상 형식이 클래스라면 객체가 그 클래스의 인스턴스가 아닐 때 발생합니다. 대상 형식이 인터페이스라면 객체가 해당 인터페이스를 구현하지 않을 때 발생합니다. 객체 자신의 클래스나 호환되는 상위 클래스 또는 인터페이스로 캐스트하면 성공합니다.
Java에서 ClassCastException을 피하려면 어떻게 해야 하나요?
선언 형식을 구체적으로 지정하고 제네릭을 사용하세요. 런타임 형식이 이미 보장되지 않는 다운캐스트 전에는 instanceof로 확인하세요. 이런 방법은 안전하지 않은 캐스트를 줄여 주지만 모든 임의의 캐스트를 유효하게 만들지는 않습니다.
이 예외를 처리하지 않으면 어떻게 되나요?
예외를 잡지 않으면 현재 스레드의 호출 스택을 따라 전파되어 해당 스레드의 작업이 종료될 수 있습니다. 애플리케이션 전체에 미치는 영향은 스레드와 주변의 오류 처리 방식에 따라 달라집니다.
try-catch로 ClassCastException을 잡을 수 있나요?
네. 복구가 의미 있을 때 잡으세요. 그렇지 않다면 호환되지 않는 캐스트나 잘못된 형식 가정의 원인을 바로잡으세요.
instanceof를 사용하는 것이 좋은 방법인가요?
객체의 런타임 하위 형식에 따라 동작이 실제로 달라질 때 사용하세요. 코드 곳곳에 형식 검사가 반복된다면 하위 형식별 동작을 다형성 메서드로 옮기거나 데이터 모델을 다시 설계하는 방법을 고려하세요.