HOWTO · Java
java.lang.ClassCastException en Java
Este artículo lo ayudará a manejar la java.lang.ClassCastException en Java.
En esta página
Una java.lang.ClassCastException indica que el tipo real de un objeto en tiempo de ejecución no es compatible con el tipo solicitado en una conversión. Para corregirlo, compara el tipo real del objeto con el tipo de destino y luego usa instanceof, genéricos con tipos definidos o un diseño diferente para que la conversión sea válida. Captura la excepción solo cuando el programa pueda recuperarse de forma útil.
¿Qué es una ClassCastException?
Java permite una conversión cuando la relación entre los tipos hace posible que sea válida en tiempo de ejecución. La conversión no cambia el objeto. En tiempo de ejecución, Java comprueba si el objeto es una instancia de la clase de destino o implementa la interfaz de destino. Si no es así, Java lanza java.lang.ClassCastException.
Por ejemplo, si Dog y Cat extienden Animal, una referencia Animal puede apuntar a cualquiera de los dos objetos. Convertir una referencia Animal a Dog solo es válido si el objeto al que apunta es realmente un Dog. Un objeto Cat no se puede convertir a su tipo hermano, Dog. En cambio, asignar un Dog a una referencia Animal es una conversión ascendente y es seguro, porque todo Dog es un Animal.
¿Por qué Java lanza una ClassCastException?
La causa habitual es suponer que una referencia de tipo general contiene una instancia de una subclase determinada. El compilador puede comprobar que una conversión descendente sea estructuralmente posible, pero no siempre puede saber qué objeto habrá en esa referencia en tiempo de ejecución. La comprobación en tiempo de ejecución detecta la incompatibilidad.
Esto suele ocurrir cuando el código recibe valores como Object, utiliza una colección antigua sin genéricos o presupone cuál es la clase concreta que devuelve una API. El polimorfismo en sí no es el problema: llamar a un método sobrescrito mediante una referencia a la clase padre es seguro. El error ocurre cuando el código intenta convertir esa referencia a un subtipo que el objeto no tiene.
Las colecciones sin tipos y las conversiones no verificadas también pueden eludir las comprobaciones de tipos de los genéricos. Al recuperar un valor más adelante, puede insertarse una conversión implícita que falle cuando se use ese valor. Busca dónde se añadió el valor a la colección y dónde se supuso su tipo; cambiar solo la línea que lanza la excepción puede ocultar el origen de la incompatibilidad.
Cómo prevenir una ClassCastException
Procura declarar las referencias y colecciones con el tipo útil más específico. Los genéricos permiten que el compilador compruebe las inserciones y recuperaciones de elementos de una colección, lo que reduce la necesidad de hacer conversiones. No vuelven segura la conversión explícita de cualquier objeto; además, los tipos sin genéricos y las operaciones no verificadas aún pueden eludir esa protección.
Una conversión descendente puede compilar cuando las clases de origen y destino están relacionadas, pero fallar si el objeto es en realidad una instancia de un subtipo hermano. Este ejemplo captura el error para que el programa pueda terminar normalmente:
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
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
El bloque catch recibe java.lang.ClassCastException: el objeto es un Cat, no un Dog. El mensaje y el seguimiento de pila generados por la JVM pueden variar según el entorno de ejecución; para diagnosticar la incompatibilidad, fíjate en el tipo de excepción y en la línea de la conversión. Mantén el valor tipado como Animal cuando no necesites un comportamiento específico de un subtipo.
Si el tipo de un valor es deliberadamente general, comprueba su tipo en tiempo de ejecución antes de convertirlo a uno más específico. Este ejemplo autónomo muestra una conversión segura:
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());
}
}
}
La conversión está protegida por instanceof, por lo que solo se realiza si el objeto es un Dog. Si no necesitas un comportamiento específico del subtipo, conserva el valor como Animal y utiliza la API que comparten sus subtipos. En las versiones de Java que admiten la coincidencia de patrones con instanceof, es posible combinar la comprobación y la asignación a una variable; este ejemplo utiliza la sintaxis tradicional para ofrecer mayor compatibilidad.
Cómo diagnosticar y manejar una ClassCastException
Lee el mensaje de la excepción y el seguimiento de pila para identificar el tipo de origen, el tipo de destino solicitado y la línea donde ocurre la conversión. Después, sigue el valor hasta su origen: inspecciona el objeto devuelto, el contenido de la colección, la asignación durante la deserialización o el contrato de la API. Añade una comprobación de tipos o corrige el modelo de datos declarado donde se hace la suposición.
Un bloque try-catch es apropiado cuando una conversión fallida es una condición de entrada prevista y el programa puede recuperarse; por ejemplo, si puede rechazar un registro mal formado y seguir procesando los demás. Capturar ClassCastException alrededor de la lógica habitual de la aplicación y continuar sin corregir la suposición puede dejar los datos incompletos u ocultar un error. Mantén acotado el bloque try e informa del contexto necesario para diagnosticar el valor problemático. Aquí no se muestra el mensaje de ninguna excepción, porque su redacción puede variar según la implementación y la versión de Java.
Preguntas frecuentes
¿Qué es java.lang.ClassCastException?
Para una clase de destino, Java lanza esta excepción cuando el objeto no es una instancia de esa clase. Para una interfaz de destino, la lanza cuando el objeto no implementa esa interfaz. La conversión funciona si el objeto es de esa misma clase o de una superclase o interfaz compatible.
¿Cómo puedo evitar ClassCastException en Java?
Usa tipos declarados precisos y genéricos, y comprueba con instanceof antes de una conversión descendente cuando el tipo en tiempo de ejecución no esté garantizado. Estas medidas reducen las conversiones inseguras, pero no hacen válida cualquier conversión arbitraria.
¿Qué ocurre si no la manejo?
Si no se captura, la excepción se propaga por la pila de llamadas del hilo actual y puede terminar el trabajo de ese hilo. El efecto en toda la aplicación depende del hilo y del tratamiento de errores que lo rodea.
¿Se puede capturar ClassCastException con try-catch?
Sí. Captúrala cuando sea posible recuperarse de forma significativa. De lo contrario, corrige la conversión incompatible o el origen de la suposición de tipo incorrecta.
¿Es recomendable usar instanceof?
Úsalo cuando el comportamiento dependa realmente del subtipo del objeto en tiempo de ejecución. Si las comprobaciones de tipos se repiten por todo el código, considera trasladar el comportamiento específico de cada subtipo a métodos polimórficos o rediseñar el modelo de datos.