Downcasting in Java
Asked Answered
N

12

203

Upcasting is allowed in Java, however downcasting gives a compile error.

The compile error can be removed by adding a cast but would anyway break at the runtime.

In this case why Java allows downcasting if it cannot be executed at the runtime?
Is there any practical use for this 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");
  }
}
Nightie answered 19/12, 2008 at 12:11 Comment(5)
An example snippet of code plus the error would make this a better question for people who are trying to learn the concepts.Reeher
I see example above is taken from velocityreviews.com/forums/t151266-downcasting-problem.html which has some good answers already.Cordell
@Cordell - Joel's main intention was to get all the great question and answers under one common umbrella. It doesn't matter if the question/code/answers are already posted in some other sites. I hope you get the point, else listen to Joel's podcasts.Mcgaha
Please edit this so that the code snippets are all indented by four spaces. That will fix the formatting.Check
B b = (B) new A(); is illegal, you should not call this as a downcast. When we talk about up/down cast, the actual object is not changed, it is just a matter of what type of variable refer to that object. You can not use a variable of sub type refer to a object of base type.Hesitate
M
325

Downcasting is allowed when there is a possibility that it succeeds at run time:

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

In some cases this will not succeed:

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

When a cast (such as this last one) fails at runtime a ClassCastException will be thrown.

In other cases it will work:

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

Note that some casts will be disallowed at compile time, because they will never succeed at all:

Integer i = getSomeInteger();
String s = (String) i; // the compiler will not allow this, since i can never reference a String.
Mylesmylitta answered 19/12, 2008 at 12:20 Comment(12)
Object o = new Object(); String s = (String) o; It's working fine for me.. :O How?Hooke
@UnKnown: it shouldn't. Double check that you actually compiled and ran that version and if you can still reproduce it, post a separate question (with an SSCCE).Mylesmylitta
@JoachimSauer what you mean by that version? I'm using Java 8.Hooke
@UnKnown: I mean the code you posted should not run (it will compile, but throw an exception at runtime). These comments are not the space to debug that. Please post a separate question.Mylesmylitta
How casting fails at runtime? Sets the target object reference to null? Throws an exception?Nika
@Nika throws a cannot be cast exception.Bounteous
What's the difference of first example and second one that make different result? What was returned by getSomeObject()?Boulevard
@JoachimSauer Your edit is inserted at the wrong lineNarration
@Gibson: indeed, Interesting that no one else noticed that for so long. Note that you could always suggest an edit yourself to fix it.Mylesmylitta
@JoachimSauer Oh I see, I thought editing was reserved for super high point users, I guess I'll try that next time. Appreciate the fast feedbackNarration
@Gibson: with 2000 reputation you can apply an edit immediately, but you can suggest one much earlier. Suggested edits are voted on by the community.Mylesmylitta
Integer to String in last example is not related to downcastingPurtenance
R
18

Using your example, you could do:

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();
}
Rudderhead answered 19/12, 2008 at 13:8 Comment(1)
I just learned importance of instanceof when my abstract class was being extended by multiple classes and I wanted to use exclusive methods of those classes, while referring to abstract class type. Not using instanceof I had class cast exceptionVilla
R
17

I believe this applies to all statically typed languages:

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

The typecast effectively says: assume this is a reference to the cast class and use it as such. Now, lets say o is really an Integer, assuming this is a String makes no sense and will give unexpected results, thus there needs to be a runtime check and an exception to notify the runtime environment that something is wrong.

In practical use, you can write code working on a more general class, but cast it to a subclass if you know what subclass it is and need to treat it as such. A typical example is overriding Object.equals(). Assume we have a class for Car:

@Override
boolean equals(Object o) {
    if(!(o instanceof Car)) return false;
    Car other = (Car)o;
    // compare this to other and return
}
Rudderhead answered 19/12, 2008 at 12:21 Comment(1)
I like the word Really and i will edit your post to make it more apparenteShulock
U
5

We can all see that the code you provided won't work at run time. That's because we know that the expression new A() can never be an object of type B.

But that's not how the compiler sees it. By the time the compiler is checking whether the cast is allowed, it just sees this:

variable_of_type_B = (B)expression_of_type_A;

And as others have demonstrated, that sort of cast is perfectly legal. The expression on the right could very well evaluate to an object of type B. The compiler sees that A and B have a subtype relation, so with the "expression" view of the code, the cast might work.

The compiler does not consider the special case when it knows exactly what object type expression_of_type_A will really have. It just sees the static type as A and considers the dynamic type could be A or any descendant of A, including B.

Ulla answered 19/12, 2008 at 15:41 Comment(0)
G
3

In this case why Java allows downcasting if it cannot be executed at the runtime?

I believe this is because there is no way for the compiler to know at compile-time if the cast will succeed or not. For your example, it's simple to see that the cast will fail, but there are other times where it is not so clear.

For instance, imagine that types B, C, and D all extend type A, and then a method public A getSomeA() returns an instance of either B, C or D depending on a randomly generated number. The compiler cannot know which exact run-time type will be returned by this method, so if you later cast the results to B, there is no way to know if the cast will succeed (or fail). Therefore the compiler has to assume casts will succeed.

Groggery answered 19/12, 2008 at 14:17 Comment(0)
A
2

@ Original Poster - see inline comments.

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");
    }
}
Ardeth answered 16/3, 2010 at 11:25 Comment(0)
H
2

Downcast works in the case when we are dealing with an upcasted object. Upcasting:

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

So now this objValue variable can always be downcasted to int because the object which was cast is an Integer,

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

but because objValue is an Object it cannot be cast to String because int cannot be cast to String.

Hogen answered 1/7, 2013 at 6:11 Comment(0)
C
0

Downcasting is very useful in the following code snippet I use this all the time. Thus proving that downcasting is useful.

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

I store String in the Linked List. When I retrieve the elements of Linked List, Objects are returned. To access the elements as Strings(or any other Class Objects), downcasting helps me.

Java allows us to compile downcast code trusting us that we are doing the wrong thing. Still if humans make a mistake, it is caught at runtime.

Crackle answered 13/10, 2013 at 4:57 Comment(1)
Using non-generic collections in Java are the equivalent of void* pointers in C++. It doesn't sound to me like a good idea at all.Obeng
F
0

Consider the below example

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

here we create the object of subclass Bone and assigned it to super class AOne reference and now superclass reference does not know about the method method2 in the subclass i.e Bone during compile time.therefore we need to downcast this reference of superclass to subclass reference so as the resultant reference can know about the presence of methods in the subclass i.e Bone

Festoonery answered 8/6, 2014 at 4:59 Comment(1)
AOne looks somewhat confusing. Please consider changing your class names to Dog and Animal or somethingNostology
R
0

Downcasting transformation of objects is not possible. Only

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

is posible

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

}
Remora answered 30/7, 2019 at 9:24 Comment(0)
W
0

To do downcasting in Java, and avoid run-time exceptions, take a reference of the following code:

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

Here, Animal is the parent class and Dog is the child class.
instanceof is a keyword that is used for checking if a reference variable is containing a given type of object reference or not.

Walkabout answered 6/9, 2019 at 10:59 Comment(0)
P
0

I will tell you why this happened. First of all you have to understand how JVM supports when we assign parent class into the child class using downcasting, because of reference . For example consider in the following code.

A is the super type any class that extends from it and can store the reference B class.
    A a =new B();
When you assign a reference variable into the child class jvm will understand that since A can store the reference of B class that is why you can do it.
B b=(B)b;
  1. The reason which is called compile time error and why you couldn't directly assign Parent class into the Child class because there is not any extends relationship. Note that casting only occurring with the key which is called extends, that is why you receive the compile time error.
  2. Another reason which is called ClassCastException by the runtime because of jvm it directly accept the rule which is okay I accept that it is true but jvm after that will understand that by the runtime it is not store any referance of Child class when code was writing by the programmer who write coding in the syntax .
Pricefixing answered 18/5, 2022 at 20:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.