Downcasting en Java

182

L'upcasting est autorisé en Java, mais le downcasting donne une erreur de compilation.

L'erreur de compilation peut être supprimée en ajoutant une distribution mais serait de toute façon interrompue au moment de l'exécution.

Dans ce cas, pourquoi Java autorise le downcasting s'il ne peut pas être exécuté à l'exécution?
Y a-t-il une utilisation pratique de ce concept?

public class demo {
  public static void main(String a[]) {
      B b = (B) new A(); // compiles with the cast, 
                         // but runtime exception - java.lang.ClassCastException
  }
}

class A {
  public void draw() {
    System.out.println("1");
  }

  public void draw1() {
    System.out.println("2");
  }
}

class B extends A {
  public void draw() {
    System.out.println("3");
  }
  public void draw2() {
    System.out.println("4");
  }
}
guerrier
la source
9
Un exemple d'extrait de code et l'erreur feraient de cette question une meilleure question pour les personnes qui essaient d'apprendre les concepts.
Bob Cross
3
+1 pour le commentaire de Bob. La question n'est pas du tout claire.
Jon Skeet
Je vois que l'exemple ci-dessus est tiré de velocityreviews.com/forums/t151266-downcasting-problem.html qui a déjà de bonnes réponses.
PhiLho
2
@PhiLho - L'intention principale de Joel était de rassembler toutes les grandes questions et réponses sous un même parapluie. Peu importe si la question / le code / les réponses sont déjà publiés sur d'autres sites. J'espère que vous comprenez, sinon écoutez les podcasts de Joel.
Omnipotent
Veuillez modifier ceci afin que les extraits de code soient tous mis en retrait de quatre espaces. Cela corrigera le formatage.
slim

Réponses:

301

Le downcasting est autorisé lorsqu'il est possible qu'il réussisse au moment de l'exécution:

Object o = getSomeObject(),
String s = (String) o; // this is allowed because o could reference a String

Dans certains cas, cela ne réussira pas:

Object o = new Object();
String s = (String) o; // this will fail at runtime, because o doesn't reference a String

Lorsqu'un casting (tel que celui-ci) échoue à l'exécution, un ClassCastExceptionsera lancé.

Dans d'autres cas, cela fonctionnera:

Object o = "a String";
String s = (String) o; // this will work, since o references a String

Notez que certains casts seront interdits au moment de la compilation, car ils ne réussiront jamais du tout:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
Joachim Sauer
la source
Object o = new Object(); String s = (String) o;Cela fonctionne bien pour moi ..: O Comment?
Asif Mushtaq
@ Inconnu: ça ne devrait pas. Vérifiez que vous avez réellement compilé et exécuté cette version et si vous pouvez toujours la reproduire, postez une question distincte (avec un SSCCE ).
Joachim Sauer
@JoachimSauer qu'entendez-vous par cette version? J'utilise Java 8.
Asif Mushtaq
1
@UnKnown: Je veux dire que le code que vous avez publié ne doit pas s'exécuter (il se compilera, mais lèvera une exception lors de l'exécution). Ces commentaires ne sont pas l'espace pour déboguer cela. Veuillez poster une question distincte.
Joachim Sauer
Comment la diffusion échoue à l'exécution? Définit la référence de l'objet cible sur null? Lève une exception?
CygnusX1
17

En utilisant votre exemple, vous pourriez faire:

public void doit(A a) {
    if(a instanceof B) {
        // needs to cast to B to access draw2 which isn't present in A
        // note that this is probably not a good OO-design, but that would
        // be out-of-scope for this discussion :)
        ((B)a).draw2();
    }
    a.draw();
}
Rolf Rander
la source
Je viens d'apprendre l'importance de instanceof lorsque ma classe abstraite était étendue par plusieurs classes et je voulais utiliser des méthodes exclusives de ces classes, tout en faisant référence au type de classe abstraite. Ne pas utiliser instanceof I had class cast exception
Tarun
16

Je pense que cela s'applique à toutes les langues typées statiquement:

String s = "some string";
Object o = s; // ok
String x = o; // gives compile-time error, o is not neccessarily a string
String x = (String)o; // ok compile-time, but might give a runtime exception if o is not infact a String

Le typage dit effectivement: supposons qu'il s'agit d'une référence à la classe cast et utilisez-la comme telle. Maintenant, disons que o est vraiment un Integer, supposer qu'il s'agit d'une chaîne n'a aucun sens et donnera des résultats inattendus, il doit donc y avoir une vérification d'exécution et une exception pour notifier l'environnement d'exécution que quelque chose ne va pas.

Dans la pratique, vous pouvez écrire du code en travaillant sur une classe plus générale, mais le convertir en sous-classe si vous savez de quelle sous-classe il s'agit et devez la traiter comme telle. Un exemple typique est de surcharger Object.equals (). Supposons que nous ayons une classe pour la voiture:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}
Rolf Rander
la source
J'aime le mot vraiment et je modifierai votre message pour le rendre plus apparent
Charaf JRA
5

Nous pouvons tous voir que le code que vous avez fourni ne fonctionnera pas au moment de l'exécution. C'est parce que nous savons que l'expression nenew A() peut jamais être un objet de type B.

Mais ce n'est pas ainsi que le compilateur le voit. Au moment où le compilateur vérifie si la distribution est autorisée, il voit simplement ceci:

variable_of_type_B = (B)expression_of_type_A;

Et comme d'autres l'ont démontré, ce genre de distribution est parfaitement légal. L'expression de droite pourrait très bien s'évaluer comme un objet de type B. Le compilateur voit cela Aet Ba une relation de sous-type, donc avec la vue "expression" du code, le cast peut fonctionner.

Le compilateur ne considère pas le cas spécial lorsqu'il sait exactement quel type d'objet expression_of_type_Aaura réellement. Il voit simplement le type statique comme Aet considère que le type dynamique pourrait être Aou tout descendant de A, y compris B.

Rob Kennedy
la source
3

Dans ce cas, pourquoi Java autorise le downcasting s'il ne peut pas être exécuté au moment de l'exécution?

Je crois que c'est parce qu'il n'y a aucun moyen pour le compilateur de savoir au moment de la compilation si la distribution réussira ou non. Pour votre exemple, il est simple de voir que la distribution échouera, mais il y a d'autres moments où ce n'est pas si clair.

Par exemple, imaginez que les types B, C et D étendent tous le type A, puis une méthode public A getSomeA()renvoie une instance de B, C ou D en fonction d'un nombre généré aléatoirement. Le compilateur ne peut pas savoir quel type d'exécution exact sera retourné par cette méthode, donc si vous convertissez ultérieurement les résultats en B, il n'y a aucun moyen de savoir si la conversion réussira (ou échouera). Par conséquent, le compilateur doit supposer que les transtypages réussiront.

mat b
la source
2

@ Affiche originale - voir les commentaires en ligne.

public class demo 
{
    public static void main(String a[]) 
    {
        B b = (B) new A(); // compiles with the cast, but runtime exception - java.lang.ClassCastException 
        //- A subclass variable cannot hold a reference to a superclass  variable. so, the above statement will not work.

        //For downcast, what you need is a superclass ref containing a subclass object.
        A superClassRef = new B();//just for the sake of illustration
        B subClassRef = (B)superClassRef; // Valid downcast. 
    }
}

class A 
{
    public void draw() 
    {
        System.out.println("1");
    }

    public void draw1() 
    {
        System.out.println("2");
    }
}

class B extends A 
{
    public void draw() 
    {
        System.out.println("3");
    }

    public void draw2() 
    {
        System.out.println("4");
    }
}
Alok Sharma
la source
2

Downcast fonctionne dans le cas où nous avons affaire à un objet upcasted. Upcasting:

int intValue = 10;
Object objValue = (Object) intvalue;

Alors maintenant, cette objValuevariable peut toujours être abaissée intcar l'objet qui a été casté est un Integer,

int oldIntValue = (Integer) objValue;
// can be done 

mais parce que objValuec'est un objet vers lequel il ne peut pas être casté Stringcar intil ne peut pas être casté String.

Uday Reddy
la source
0

Le downcasting est très utile dans l'extrait de code suivant que j'utilise tout le temps. Prouver ainsi que le downcasting est utile.

private static String printAll(LinkedList c)
{
    Object arr[]=c.toArray();
    String list_string="";
    for(int i=0;i<c.size();i++)
    {
        String mn=(String)arr[i];
        list_string+=(mn);
    }
    return list_string;
}

Je stocke String dans la liste liée. Lorsque je récupère les éléments de la liste liée, les objets sont renvoyés. Pour accéder aux éléments en tant que chaînes (ou tout autre objet de classe), le downcasting m'aide.

Java nous permet de compiler du code downcast en nous faisant confiance que nous faisons la mauvaise chose. Pourtant, si les humains font une erreur, elle est interceptée au moment de l'exécution.

Drishti
la source
L'utilisation de collections non génériques en Java est l'équivalent des void*pointeurs en C ++. Cela ne me semble pas du tout une bonne idée.
Jezor
0

Prenons l'exemple ci-dessous

public class ClastingDemo {

/**
 * @param args
 */
public static void main(String[] args) {
    AOne obj = new Bone();
    ((Bone) obj).method2();
}
}

class AOne {
public void method1() {
    System.out.println("this is superclass");
}
}


 class Bone extends AOne {

public void method2() {
    System.out.println("this is subclass");
}
}

ici, nous créons l'objet de la sous-classe Bone et l'assignons à la référence de la super classe AOne et maintenant la référence de la superclasse ne connaît pas la méthode method2 dans la sous-classe, c'est-à-dire Bone au moment de la compilation. la référence résultante peut connaître la présence de méthodes dans la sous-classe, c'est-à-dire os

ZohebSiddiqui
la source
AOne semble quelque peu déroutant. S'il vous plaît envisager de changer vos noms de classe en chien et animal ou quelque chose
Kartik Chugh
0

Pour effectuer un downcasting en Java et éviter les exceptions d'exécution, prenez une référence du code suivant:

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

Ici, Animal est la classe parent et Dog est la classe enfant.
instanceof est un mot-clé utilisé pour vérifier si une variable de référence contient ou non un type donné de référence d'objet.

user11949964
la source
0

La transformation descendante des objets n'est pas possible. Seulement

DownCasting1 _downCasting1 = (DownCasting1)((DownCasting2)downCasting1);

est possible

class DownCasting0 {
    public int qwe() {
        System.out.println("DownCasting0");
        return -0;
    }
}

class DownCasting1 extends DownCasting0 {
    public int qwe1() {
        System.out.println("DownCasting1");
        return -1;
    }
}

class DownCasting2 extends DownCasting1 {
    public int qwe2() {
        System.out.println("DownCasting2");
        return -2;
    }
}

public class DownCasting {

    public static void main(String[] args) {

        try {
            DownCasting0 downCasting0 = new DownCasting0();
            DownCasting1 downCasting1 = new DownCasting1();
            DownCasting2 downCasting2 = new DownCasting2();

            DownCasting0 a1 = (DownCasting0) downCasting2;
            a1.qwe(); //good

            System.out.println(downCasting0 instanceof  DownCasting2);  //false
            System.out.println(downCasting1 instanceof  DownCasting2);  //false
            System.out.println(downCasting0 instanceof  DownCasting1);  //false

            DownCasting2 _downCasting1= (DownCasting2)downCasting1;     //good
            DownCasting1 __downCasting1 = (DownCasting1)_downCasting1;  //good
            DownCasting2 a3 = (DownCasting2) downCasting0; // java.lang.ClassCastException

            if(downCasting0 instanceof  DownCasting2){ //false
                DownCasting2 a2 = (DownCasting2) downCasting0;
                a2.qwe(); //error
            }

            byte b1 = 127;
            short b2 =32_767;
            int b3 = 2_147_483_647;
//          long _b4 = 9_223_372_036_854_775_807; //int large number max 2_147_483_647
            long b4 = 9_223_372_036_854_775_807L;
//          float _b5 = 3.4e+038; //double default
            float b5 = 3.4e+038F; //Sufficient for storing 6 to 7 decimal digits
            double b6 = 1.7e+038;
            double b7 = 1.7e+038D; //Sufficient for storing 15 decimal digits

            long c1 = b3;
            int c2 = (int)b4;

            //int       4 bytes     Stores whole numbers from -2_147_483_648 to 2_147_483_647
            //float     4 bytes     Stores fractional numbers from 3.4e−038 to 3.4e+038. Sufficient for storing 6 to 7 decimal digits
            float c3 = b3; //logic error
            double c4 = b4; //logic error


        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

}
Aliaksandr Shpak
la source