saravanan
public class Animal {
public void eat() {}
}
public class Dog extends Animal {
public void eat() {}
public void main(String[] args) {
Animal animal = new Animal();
Dog dog = (Dog) animal;
}
}
La asignación Dog dog = (Dog) animal;
no genera un error de compilación, pero en tiempo de ejecución genera un ClassCastException
. ¿Por qué el compilador no puede detectar este error?
miguel baya
Al usar un molde, esencialmente le estás diciendo al compilador “confía en mí. Soy un profesional, sé lo que hago y sé que aunque no puedes garantizarlo, te digo que esto animal
La variable definitivamente va a ser un perro”.
Dado que el animal no es en realidad un perro (es un animal, podrías hacer Animal animal = new Dog();
y sería un perro) la máquina virtual lanza una excepción en el tiempo de ejecución porque ha violado esa confianza (¡le dijo al compilador que todo estaría bien y no lo está!)
El compilador es un poco más inteligente que simplemente aceptar todo a ciegas, si intenta convertir objetos en diferentes jerarquías de herencia (por ejemplo, convertir un perro en una cadena), entonces el compilador se lo devolverá porque sabe que eso nunca podría funcionar.
Debido a que esencialmente solo está evitando que el compilador se queje, cada vez que lanza es importante verificar que no cause un ClassCastException
mediante el uso instanceof
en una declaración if (o algo por el estilo).
-
Gracias, pero necesitas que el perro se extienda de Animal si no, no funciona 🙂
– usuario3402040
9 dic 2015 a las 10:53
-
@delive Por supuesto que sí, pero según la pregunta,
Dog
hace extender desdeAnimal
!–Michael Berry
25 de mayo de 2017 a las 10:41
Porque teóricamente Animal animal
poder ser un perro:
Animal animal = new Dog();
Por lo general, bajar el tono no es una buena idea. Deberías evitarlo. Si lo usas, es mejor que incluyas un cheque:
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
-
pero el siguiente código genera un error de compilación Dog dog=new Animal(); (tipos incompatibles). Pero en esta situación, el compilador identifica que Animal es una superclase y Perro es una subclase. Por lo tanto, la asignación es incorrecta. Pero cuando lanzamos Perro perro = (Perro) animal; acepta .por favor explícame sobre esto
– saravanan
1 de febrero de 2011 a las 13:19
-
sí, porque Animal es superclase. No todos los animales son perros, ¿verdad? Puede hacer referencia a las clases solo por sus tipos o sus supertipos. No sus subtipos.
– Bozho
1 de febrero de 2011 a las 13:31
Para evitar este tipo de ClassCastException, si tiene:
class A
class B extends A
Puede definir un constructor en B que tome un objeto de A. De esta manera podemos hacer el “lanzado”, por ejemplo:
public B(A a) {
super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
// If B class has more attributes, then you would initilize them here
}
Zeeshan
Elaborando la respuesta dada por Michael Berry.
Dog d = (Dog)Animal; //Compiles but fails at runtime
Aquí le estás diciendo al compilador “Confía en mí. Lo sé d
en realidad se refiere a un Dog
objeto” aunque no lo es.
Recuerde que el compilador se ve obligado a confiar en nosotros cuando hacemos un downcast.
El compilador solo conoce el tipo de referencia declarado. La JVM en tiempo de ejecución sabe qué es realmente el objeto.
Entonces, cuando la JVM en el tiempo de ejecución descubre que el Dog d
en realidad se refiere a un Animal
y no un Dog
objeto dice. Oye… le mentiste al compilador y arrojas una gran ClassCastException
.
Entonces, si estás abatido, deberías usar instanceof
Prueba para no equivocarte.
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
}
Ahora nos viene una pregunta a la cabeza. ¿Por qué diablos el compilador permite el downcast cuando eventualmente va a arrojar un java.lang.ClassCastException
?
La respuesta es que todo lo que el compilador puede hacer es verificar que los dos tipos estén en el mismo árbol de herencia, por lo que dependiendo del código que haya llegado antes de la conversión a la baja, es posible que animal
es de tipo dog
.
El compilador debe permitir cosas que podrían funcionar en tiempo de ejecución.
Considere el siguiente fragmento de código:
public static void main(String[] args)
{
Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
d.eat();
}
private static Animal getMeAnAnimal()
{
Animal animal = new Dog();
return animal;
}
Sin embargo, si el compilador está seguro de que la conversión no funcionará, la compilación fallará. IE Si intenta convertir objetos en diferentes jerarquías de herencia
String s = (String)d; // ERROR : cannot cast for Dog to String
A diferencia de downcasting, upcasting funciona implícitamente porque cuando upcast está restringiendo implícitamente la cantidad de métodos que puede invocar, a diferencia de downcasting, lo que implica que más adelante, es posible que desee invocar un método más específico.
Dog d = new Dog();
Animal animal1 = d; // Works fine with no explicit cast
Animal animal2 = (Animal) d; // Works fine with n explicit cast
Ambos upcast anteriores funcionarán bien sin ninguna excepción porque un Perro ES-UN Animal, cualquier cosa que un Animal pueda hacer, un perro puede hacerlo. Pero no es cierto viceversa.
Para desarrollar la respuesta de @Caumons:
Imagine que una clase padre tiene muchos hijos y es necesario agregar un campo común a esa clase. Si considera el enfoque mencionado, debe ir a cada clase de niños uno por uno y refactorizar sus constructores para el nuevo campo. por lo tanto, esa solución no es una solución prometedora en este escenario
Ahora eche un vistazo a esta solución.
Un padre puede recibir un objeto propio de cada hijo. Aquí hay una clase padre:
public class Father {
protected String fatherField;
public Father(Father a){
fatherField = a.fatherField;
}
//Second constructor
public Father(String fatherField){
this.fatherField = fatherField;
}
//.... Other constructors + Getters and Setters for the Fields
}
Aquí está nuestra clase secundaria que debería implementar uno de sus constructores principales, en este caso el constructor mencionado anteriormente:
public class Child extends Father {
protected String childField;
public Child(Father father, String childField ) {
super(father);
this.childField = childField;
}
//.... Other constructors + Getters and Setters for the Fields
@Override
public String toString() {
return String.format("Father Field is: %s\nChild Field is: %s", fatherField, childField);
}
}
Ahora probamos la aplicación:
public class Test {
public static void main(String[] args) {
Father fatherObj = new Father("Father String");
Child child = new Child(fatherObj, "Child String");
System.out.println(child);
}
}
Y aqui esta el resultado :
Padre Campo es: Padre Cadena
El campo secundario es: Cadena secundaria
Ahora puede agregar fácilmente nuevos campos a la clase padre sin preocuparse por los códigos de sus hijos para romper;
Comunidad
El código genera un error de compilación porque su tipo de instancia es un Animal:
Animal animal=new Animal();
El downcasting no está permitido en Java por varias razones. Ver aquí para más detalles.
fresco
Como se explica, no es posible. Si desea utilizar un método de la subclase, evalúe la posibilidad de agregar el método a la superclase (puede estar vacío) y llamar desde las subclases obteniendo el comportamiento que desea (subclase) gracias al polimorfismo. Por lo tanto, cuando llame a d.method(), la llamada tendrá éxito sin necesidad de enviar, pero en caso de que el objeto no sea un perro, no habrá ningún problema.
USTED le está diciendo al compilador que NO detecte el error.
– Mauricio
1 de febrero de 2011 a las 13:14