web-dev-qa-db-fra.com

casting explicite de la super classe à la sous-classe

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'affectation Dog dog = (Dog) animal; ne génère pas d'erreur de compilation, mais génère, à l'exécution, une ClassCastException. Pourquoi le compilateur ne peut-il pas détecter cette erreur?

123
saravanan

En utilisant un casting, vous dites essentiellement au compilateur "faites-moi confiance. Je suis un professionnel, je sais ce que je fais et je sais que bien que vous ne puissiez pas le garantir, je vous dis que cette variable animal est va certainement être un chien. "

Puisque l'animal n'est pas réellement un chien (c'est un animal, vous pouvez utiliser Animal animal = new Dog(); et ce serait un chien), le VM lève une exception lors de l'exécution car vous avez violé cette confiance (vous avez tout dit au compilateur ça irait et ce n'est pas ça!)

Le compilateur est un peu plus intelligent que simplement accepter aveuglément tout. Si vous essayez de convertir des objets dans différentes hiérarchies d'héritage (attribuer un chien à une chaîne, par exemple), le compilateur le rejettera car il sait que cela ne pourrait jamais fonctionner.

Parce que vous empêchez simplement le compilateur de se plaindre, il est important de vérifier que vous ne causerez pas une ClassCastException en utilisant instanceof dans une instruction if (ou quelque chose de ce genre).

270
Michael Berry

Parce que théoriquement Animal animal peut être un chien:

Animal animal = new Dog();

En règle générale, le downcasting n'est pas une bonne idée. Tu devrais l'éviter. Si vous l'utilisez, vous feriez mieux d'inclure un chèque: 

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

Afin d'éviter ce genre de ClassCastException, si vous avez:

class A
class B extends A

Vous pouvez définir un constructeur dans B qui prend un objet de A. De cette façon, nous pouvons faire le "cast" par exemple:

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
}
37
Caumons

Le code génère une erreur de compilation car votre type instance est un animal:

Animal animal=new Animal();

Le downcasting n’est pas autorisé en Java pour plusieurs raisons . Voir ici pour plus de détails.

2
Simeon

Comme expliqué, il n’est pas possible . Si vous souhaitez utiliser une méthode de la sous-classe, évaluez la possibilité d’ajouter la méthode à la superclasse (peut être vide) et appelez les sous-classes en obtenant le comportement souhaité (sous-classe). grâce au polymorphisme . Ainsi, lorsque vous appelez d.method (), l'appel réussira sans être lancé, mais si l'objet n'est pas un chien, il n'y aura pas de problème.

1
fresko

Pour développer la réponse de @Caumons:

Imaginez qu’une classe de pères ait beaucoup d’enfants et qu’il soit nécessaire d’ajouter un .__ commun. champ dans cette classe. Si vous envisagez l'approche mentionnée, vous devriez aller à chaque classe un par un et refactoriser leurs constructeurs pour le nouveau champ . donc cette solution n'est pas une solution prometteuse dans ce scénario

Maintenant, jetez un oeil à cette solution.

Un père peut recevoir un objet personnel de chaque enfant. Voici un père classe:

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
}

Voici notre classe d'enfants qui devrait implémenter l'un de ses pères constructeur, dans ce cas le constructeur susmentionné:

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

Maintenant, nous testons l'application:

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

Et voici le résultat : 

Le champ du père est: 

Le champ enfant est: String enfant

Maintenant, vous pouvez facilement ajouter de nouveaux champs à la classe des pères sans vous soucier des codes à déchiffrer de vos enfants;

0
Mehdi