Downcasting em Java


179

A upcasting é permitida em Java, mas a downcasting gera um erro de compilação.

O erro de compilação pode ser removido adicionando uma conversão, mas seria interrompido no tempo de execução.

Nesse caso, por que o Java permite o downcasting se não pode ser executado no tempo de execução?
Existe algum uso prático para esse conceito?

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
Um exemplo de trecho de código mais o erro tornaria essa uma pergunta melhor para as pessoas que estão tentando aprender os conceitos.
— Bob Cross

3
+1 no comentário de Bob. A questão não está clara.
— Jon Skeet

Vejo o exemplo acima, obtido em velocityreviews.com/forums/t151266-downcasting-problem.html, que já tem algumas boas respostas.
— PhiLho

2
@PhiLho - A principal intenção de Joel era obter todas as ótimas perguntas e respostas sob o mesmo guarda-chuva. Não importa se a pergunta / código / respostas já foram postadas em outros sites. Espero que você entenda, ouça os podcasts de Joel.
— Omnipotent

Edite isso para que os trechos de código sejam todos recuados por quatro espaços. Isso irá corrigir a formatação.
— magro

Respostas:


298

A downcasting é permitida quando existe a possibilidade de êxito no tempo de execução:

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

Em alguns casos, isso não terá êxito:

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

Quando um elenco (como este último) falha no tempo de execução, um ClassCastExceptionserá lançado.

Em outros casos, ele funcionará:

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

Observe que algumas transmissões não serão permitidas no momento da compilação, porque nunca terão êxito:

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;Está funcionando bem para mim ..: O Como?
— Asif Mushtaq

@ Desconhecido: não deveria. Verifique novamente se você realmente compilou e executou essa versão e, se ainda conseguir reproduzi-la, poste uma pergunta separada (com um SSCCE ).
— Joachim Sauer

@JoachimSauer, o que você quer dizer com essa versão? Eu estou usando o Java 8.
— Asif Mushtaq

1
@ Desconhecido: quero dizer que o código que você postou não deve ser executado (ele será compilado, mas lançará uma exceção em tempo de execução). Esses comentários não são o espaço para depurar isso. Envie uma pergunta separada.
— Joachim Sauer

Como a transmissão falha no tempo de execução? Define a referência do objeto de destino como nula? Lança uma exceção?
— CygnusX1 18/03

17

Usando seu exemplo, você pode fazer:

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

Acabei de aprender a importância da instância de quando minha classe abstrata estava sendo estendida por várias classes e queria usar métodos exclusivos dessas classes, enquanto me referia ao tipo de classe abstrata. Não usando instanceof eu tinha exceção de elenco de classe
— Tarun

16

Eu acredito que isso se aplica a todos os idiomas estaticamente tipados:

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

O typecast efetivamente diz: assuma que essa é uma referência à classe de elenco e use-a como tal. Agora, digamos que o é realmente um número inteiro, supondo que essa seja uma string não faz sentido e dará resultados inesperados; portanto, é necessário haver uma verificação de tempo de execução e uma exceção para notificar o ambiente de tempo de execução de que algo está errado.

No uso prático, você pode escrever código trabalhando em uma classe mais geral, mas convertê-lo em uma subclasse se souber qual é a subclasse e precisar tratá-lo como tal. Um exemplo típico é substituir Object.equals (). Suponha que tenhamos uma aula para carro:

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

Eu gosto da palavra Realmente e irei editar sua postagem para torná-la mais aparente.
— Charaf JRA

5

Todos nós podemos ver que o código que você forneceu não funcionará em tempo de execução. Isso porque sabemos que a expressão nuncanew A() pode ser um objeto do tipo .B

Mas não é assim que o compilador o vê. No momento em que o compilador está verificando se o elenco é permitido, ele apenas vê o seguinte:

variable_of_type_B = (B)expression_of_type_A;

E como outros demonstraram, esse tipo de elenco é perfeitamente legal. A expressão à direita poderia muito bem ser avaliada para um objeto do tipo B. O compilador vê isso Ae Btem uma relação de subtipo, portanto, com a visualização "expressão" do código, a conversão pode funcionar.

O compilador não considera o caso especial quando sabe exatamente que tipo de objeto expression_of_type_Arealmente terá. Ele apenas vê o tipo estático como Ae considera que o tipo dinâmico pode ser Aou qualquer descendente de A, inclusive B.


3

Nesse caso, por que o Java permite downcasting se não pode ser executado no tempo de execução?

Eu acredito que isso ocorre porque não há como o compilador saber em tempo de compilação se o elenco será bem-sucedido ou não. Para o seu exemplo, é simples ver que o elenco falhará, mas há outros momentos em que não é tão claro.

Por exemplo, imagine que os tipos B, C e D estendem o tipo A e, em seguida, um método public A getSomeA()retorna uma instância de B, C ou D, dependendo de um número gerado aleatoriamente. O compilador não pode saber para qual tipo de tempo de execução exato será retornado por esse método; portanto, se você converter os resultados posteriormente B, não há como saber se a conversão será bem-sucedida (ou falhará). Portanto, o compilador deve assumir que as conversões serão bem-sucedidas.


2

@ Pôster original - ver comentários em linha.

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

O downcast funciona no caso em que estamos lidando com um objeto upcasted. Upcasting:

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

Portanto, agora essa objValuevariável sempre pode ser baixada para, intporque o objeto que foi lançado é um Integer,

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

mas porque objValueé um Objeto para o qual não pode ser convertido, Stringporque intnão pode ser convertido para String.


0

O downcasting é muito útil no seguinte snippet de código que eu uso isso o tempo todo. Provando assim que o downcasting é útil.

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

Eu armazeno String na lista vinculada. Quando recupero os elementos da lista vinculada, os objetos são retornados. Para acessar os elementos como Strings (ou qualquer outro Objeto de Classe), o downcast me ajuda.

Java nos permite compilar código de downcast confiando em que estamos fazendo a coisa errada. Ainda assim, se os humanos cometem um erro, ele é pego em tempo de execução.


O uso de coleções não genéricas em Java é o equivalente a void*ponteiros em C ++. Não me parece uma boa ideia.
— Jezor

0

Considere o exemplo abaixo

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

aqui criamos o objeto da subclasse Bone e o atribuímos à referência de superclasse AOne e agora a referência de superclasse não sabe sobre o método method2 na subclasse, ou seja, Bone durante o tempo de compilação. a referência resultante pode saber sobre a presença de métodos na subclasse, ou seja, Bone


AOne parece um pouco confuso. Por favor, considere mudar seus nomes de classe para cão e do animal ou algo
— Kartik Chugh

0

Para fazer downcasting em Java e evitar exceções em tempo de execução, faça uma referência ao seguinte código:

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

Aqui, Animal é a classe dos pais e Dog é a classe dos filhos.
instanceof é uma palavra-chave usada para verificar se uma variável de referência contém um determinado tipo de referência de objeto ou não.


0

A transformação de downcasting de objetos não é possível. Somente

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

é possível

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

}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.