Downcasting in Java


179

L'upcasting è consentito in Java, tuttavia il downcasting genera un errore di compilazione.

L'errore di compilazione può essere rimosso aggiungendo un cast ma si romperà comunque in fase di esecuzione.

In questo caso, perché Java consente il downcasting se non può essere eseguito in fase di esecuzione?
C'è qualche utilità pratica per questo concetto?

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

9
Un esempio di frammento di codice più l'errore renderebbe questa una domanda migliore per le persone che stanno cercando di apprendere i concetti.
Bob Cross

3
+1 per il commento di Bob. La domanda non è affatto chiara.
Jon Skeet,

Vedo l'esempio sopra è tratto da velocityreviews.com/forums/t151266-downcasting-problem.html che ha già delle buone risposte.
PhiLho,

2
@PhiLho - L'intenzione principale di Joel era di ottenere tutte le grandi domande e risposte sotto un unico ombrello. Non importa se la domanda / il codice / le risposte sono già pubblicati in alcuni altri siti. Spero che tu capisca il punto, altrimenti ascolta i podcast di Joel.
Onnipotente il

Modifica questo in modo che gli snippet di codice siano tutti rientrati da quattro spazi. Ciò risolverà la formattazione.
slim

Risposte:


298

Il downcasting è consentito quando esiste la possibilità che abbia esito positivo in fase di esecuzione:

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

In alcuni casi questo non avrà successo:

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

Quando un cast (come quest'ultimo) fallisce in fase di esecuzione ClassCastExceptionverrà lanciato un.

In altri casi funzionerà:

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

Si noti che alcuni cast non saranno consentiti al momento della compilazione, perché non avranno mai successo:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.

Object o = new Object(); String s = (String) o;Funziona bene per me ..: O Come?
Asif Mushtaq,

@UnKnown: non dovrebbe. Controlla di aver effettivamente compilato ed eseguito quella versione e se riesci ancora a riprodurla, pubblica una domanda separata (con un SSCCE ).
Joachim Sauer,

@JoachimSauer cosa intendi con quella versione? Sto usando Java 8.
Asif Mushtaq,

1
@UnKnown: intendo che il codice che hai pubblicato non dovrebbe essere eseguito (verrà compilato, ma genererà un'eccezione in fase di esecuzione). Questi commenti non sono lo spazio per eseguire il debug. Si prega di inviare una domanda separata.
Joachim Sauer,

In che modo il casting fallisce in fase di esecuzione? Imposta il riferimento dell'oggetto di destinazione su null? Genera un'eccezione?
CygnusX1

17

Usando il tuo esempio, potresti fare:

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

Ho appena imparato l'importanza di instanceof quando la mia classe astratta veniva estesa da più classi e volevo usare metodi esclusivi di quelle classi, facendo riferimento al tipo di classe astratta. Non usando instanceof ho avuto un'eccezione per il cast di classe
Tarun il

16

Credo che questo si applichi a tutte le lingue tipicamente statiche:

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

Il typecast dice effettivamente: supponiamo che questo sia un riferimento alla classe cast e usalo come tale. Ora, supponiamo che o sia davvero un numero intero, supponendo che questa sia una stringa non ha senso e darà risultati imprevisti, quindi è necessario un controllo di runtime e un'eccezione per notificare all'ambiente di runtime che qualcosa non va.

Nell'uso pratico, puoi scrivere codice lavorando su una classe più generale, ma esegui il cast in una sottoclasse se conosci la sottoclasse e devi trattarla come tale. Un esempio tipico è l'override di Object.equals (). Supponiamo di avere un corso per auto:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}

Mi piace davvero la parola e modificherò il tuo post per renderlo più apparente
Charaf JRA

5

Tutti possiamo vedere che il codice fornito non funzionerà in fase di esecuzione. Questo perché sappiamo che l'espressione nonnew A() può mai essere un oggetto di tipo B.

Ma non è così che lo vede il compilatore. Quando il compilatore sta verificando se il cast è permesso, vede solo questo:

variable_of_type_B = (B)expression_of_type_A;

E come altri hanno dimostrato, quel tipo di cast è perfettamente legale. L'espressione a destra potrebbe benissimo valutare un oggetto di tipo B. Il compilatore lo vede Ae Bha una relazione di sottotipo, quindi con la vista "espressione" del codice, il cast potrebbe funzionare.

Il compilatore non considera il caso speciale quando sa esattamente quale tipo di oggetto expression_of_type_Aavrà davvero. Vede solo il tipo statico come Ae considera che il tipo dinamico potrebbe essere Ao qualsiasi discendente di A, incluso B.


3

In questo caso, perché Java consente il downcasting se non può essere eseguito in fase di esecuzione?

Credo che ciò sia dovuto al fatto che il compilatore non può sapere in fase di compilazione se il cast avrà esito positivo o meno. Per il tuo esempio, è semplice vedere che il cast fallirà, ma ci sono altre volte in cui non è così chiaro.

Ad esempio, immagina che i tipi B, C e D estendano tutti il ​​tipo A, quindi un metodo public A getSomeA()restituisce un'istanza di B, C o D a seconda di un numero generato casualmente. Il compilatore non può sapere a quale esatto tipo di runtime verrà restituito questo metodo, quindi se in seguito si esegue il cast dei risultati B, non è possibile sapere se il cast avrà esito positivo o negativo. Pertanto, il compilatore deve presumere che i cast abbiano esito positivo.


2

@ Poster originale - vedi commenti in linea.

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

2

Il downcast funziona nel caso in cui abbiamo a che fare con un oggetto upcasted. upcasting:

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

Quindi ora questa objValuevariabile può sempre essere downcastata intperché l'oggetto che è stato lanciato è un Integer,

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

ma poiché objValueè un oggetto su cui non può essere lanciato Stringperché intnon può essere lanciato String.


0

Il downcasting è molto utile nel seguente frammento di codice che uso sempre. Dimostrando così che il downcasting è 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;
}

Memorizzo String nell'elenco collegato. Quando recupero gli elementi dell'elenco collegato, vengono restituiti gli oggetti. Per accedere agli elementi come String (o qualsiasi altro oggetto classe), il downcasting mi aiuta.

Java ci consente di compilare il codice downcast fidandoci che stiamo facendo la cosa sbagliata. Tuttavia, se gli umani commettono un errore, viene colto in fase di esecuzione.


L'uso di raccolte non generiche in Java è l'equivalente dei void*puntatori in C ++. Non mi sembra affatto una buona idea.
Jezor,

0

Considera l'esempio seguente

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

qui creiamo l'oggetto della sottoclasse Bone e lo assegniamo al riferimento AOne della superclasse e ora il riferimento alla superclasse non è a conoscenza del metodo method2 nella sottoclasse, ovvero Bone durante il tempo di compilazione. Pertanto, dobbiamo eseguire il downcast di questo riferimento della superclasse al riferimento della sottoclasse in modo che il riferimento risultante può conoscere la presenza di metodi nella sottoclasse, ovvero Bone


AOne sembra un po 'confuso. Ti preghiamo di considerare di cambiare il nome della tua classe in Cane e animale o qualcosa del genere
Kartik Chugh,

0

Per eseguire il downcasting in Java ed evitare eccezioni di runtime, prendere un riferimento al seguente codice:

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

Qui, Animal è la classe genitore e Dog è la classe figlio.
instanceof è una parola chiave che viene utilizzata per verificare se una variabile di riferimento contiene o meno un determinato tipo di riferimento oggetto.


0

La trasformazione downcasting di oggetti non è possibile. Solo

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

è posibile

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.