Domanda

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;
    }
}

L'incarico Dog dog = (Dog) animal; non genera un errore di compilazione, ma in fase di esecuzione genera a ClassCastException. Perché il compilatore non può rilevare questo errore?

È stato utile?

Soluzione

Usando un cast stai essenzialmente dicendo al compilatore "fidati di me. Sono un professionista, so cosa sto facendo e so che anche se non puoi garantirlo, ti sto dicendo che questo animal La variabile sarà sicuramente un cane ".

Dal momento che l'animale non è in realtà un cane (è un animale, potresti farlo Animal animal = new Dog(); E sarebbe un cane) la VM lancia un'eccezione in fase di esecuzione perché hai violato quella fiducia (hai detto al compilatore che sarebbe tutto ok e non lo è!)

Il compilatore è un po 'più intelligente che accettare alla cieca tutto, se provi a lanciare oggetti in diverse gerarchie di eredità (lancia un cane ad esempio su una corda), il compilatore te lo resterà indietro perché sa che non potrebbe mai funzionare.

Perché essenzialmente stai solo impedendo al compilatore di lamentarti, ogni volta che lanci è importante verificare che non causerà un ClassCastException usando instanceof in un'istruzione if (o qualcosa del genere in tal senso.)

Altri suggerimenti

Perché teoricamente Animal animal Potere Sii un cane:

Animal animal = new Dog();

Generalmente, il downcasting non è una buona idea. Dovresti evitarlo. Se lo usi, è meglio che includi un assegno:

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}

Per evitare questo tipo di ClasscastException, se hai:

class A
class B extends A

Puoi definire un costruttore in B che prende un oggetto di A. In questo modo possiamo fare il "cast" ad es.

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
}

Elaborando la risposta data da Michael Berry.

Dog d = (Dog)Animal; //Compiles but fails at runtime

Qui stai dicendo al compilatore "fidati di me. Lo so d si riferisce davvero a a Dog oggetto "Anche se non lo è.Ricorda che il compilatore è costretto a fidarsi di noi quando facciamo un abbassamento.

Il compilatore conosce solo il tipo di riferimento dichiarato. JVM in Runtime sa quale sia realmente l'oggetto.

Quindi quando il jvm in runtime scopre che il Dog d si riferisce effettivamente a un Animal e non a Dog oggetto dice. Hey ... hai mentito al compilatore e lancia un grasso grosso ClassCastException.

Quindi, se stai abbassando, dovresti usare instanceof Prova per evitare di rovinare.

if (animal instanceof Dog) { Dog dog = (Dog) animal; }

Ora ci viene in mente una domanda. Perché il compilatore infernale sta permettendo il downcast quando alla fine lancerà un java.lang.ClassCastException?

La risposta è che tutto il compilatore può fare è verificare che i due tipi siano nello stesso albero ereditario, quindi a seconda di qualunque codice possa essere arrivato prima del downcast, è possibile che animal è di tipo dog.

Il compilatore deve consentire cose che potrebbero funzionare in fase di esecuzione.

Considera il seguente cecchino:

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;
}

Tuttavia, se il compilatore è sicuro che il cast non sarebbe possibile funzionare, la compilazione fallirà. Cioè se provi a lanciare oggetti in diverse gerarchie ereditarie

String s = (String)d; // ERROR : cannot cast for Dog to String

A differenza del downcasting, Upcasting Works implicitamente perché quando si è implicitamente limitando il numero di metodi che puoi invocare, opposto al downcasting, il che implica che in seguito potresti voler invocare un metodo più specifico.

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

Entrambi gli upcast di cui sopra funzionano bene senza alcuna eccezione perché un cane è un animale, che può fare un animale, un cane può fare. Ma non è vero Vica-Versa.

Il codice genera un errore di compilazione perché il tuo Tipo di istanza è un animale:

Animal animal=new Animal();

Il downcasting non è consentito in Java per diversi motivi. Vedere qui per dettagli.

Come spiegato, non è possibile. Se si desidera utilizzare un metodo della sottoclasse, valutare la possibilità di aggiungere il metodo alla superclasse (può essere vuoto) e chiamare dalle sottoclassi che ottengono il comportamento che desideri (sottoclasse) grazie al polimorfismo. Quindi quando chiami D.Method () la chiamata avrà successo con il casting, ma nel caso in cui l'oggetto non sarà un cane, non ci sarà un problema

Per sviluppare la risposta di @caumons:

Immagina che una classe padre abbia molti figli e c'è bisogno di aggiungere un campo comune in quella classe. Se consideri l'approccio menzionato, dovresti andare alla classe di ogni bambino uno per uno e refactor i loro costruttori per il nuovo campo. Pertanto quella soluzione non è una soluzione promettente in questo scenario

Ora dai un'occhiata a questa soluzione.

Un padre può ricevere un oggetto di sé da ogni bambino. Ecco una classe 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
}

Ecco la nostra classe figlio che dovrebbe implementare uno di suo padre costruttore, in questo caso il summenzionato costruttore:

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);
    }
}

Ora teschiamo l'applicazione:

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);
    }
}

E questo è il risultato :

Padre Field è: Padre String

Il campo per bambini è: stringa figlio

Ora puoi facilmente aggiungere nuovi campi a Father Class senza preoccuparti dei codici dei tuoi figli;

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top