Downcasting in Java

Downcasting in Java

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.

What is the difference between up-casting and down-casting with respect to class variable

Upcasting is casting to a supertype, while downcasting is casting to a subtype. Upcasting is always allowed, but downcasting involves a type check and can throw a ClassCastException.

In your case, a cast from a Dog to an Animal is an upcast, because a Dog is-a Animal. In general, you can upcast whenever there is an is-a relationship between two classes.

Downcasting would be something like this:

Animal animal = new Dog();
Dog castedDog = (Dog) animal;

Basically what you're doing is telling the compiler that you know what the runtime type of the object really is. The compiler will allow the conversion, but will still insert a runtime sanity check to make sure that the conversion makes sense. In this case, the cast is possible because at runtime animal is actually a Dog even though the static type of animal is Animal.

However, if you were to do this:

Animal animal = new Animal();
Dog notADog = (Dog) animal;

You'd get a ClassCastException. The reason why is because animal's runtime type is Animal, and so when you tell the runtime to perform the cast it sees that animal isn't really a Dog and so throws a ClassCastException.

To call a superclass's method you can do super.method() or by performing the upcast.

To call a subclass's method you have to do a downcast. As shown above, you normally risk a ClassCastException by doing this; however, you can use the instanceof operator to check the runtime type of the object before performing the cast, which allows you to prevent ClassCastExceptions:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog) {
// Guaranteed to succeed, barring classloader shenanigans
Dog castedDog = (Dog) animal;
}

Downcasts can be expressed more succinctly starting from Java 16, which introduced pattern matching for instanceof:

Animal animal = getAnimal(); // Maybe a Dog? Maybe a Cat? Maybe an Animal?
if (animal instanceof Dog castedDog) {
// now castedDog is available here as in the example above
}

Downcasting Objects to references in java

Object obj = new Reference();

This is an upcasting scenario.


If you want downcasting, then it will be,

Reference ref = (Reference) obj;

When it works/when it is possible,

  • Suppose, you create an Object class instance using Reference class (child class reference)
Object obj = new Reference();

This is an upcasting. And you can do that,

  • In this case, you can downcast without any ClassCastException
Reference ref = (Reference) obj;
  • But when it is not clear to you, is it Cast safe or not? This time instanceof helps you. So the full working code should be-
class Reference extends Object{ //not necessary extends Object class, but for 
//clearification
...

public static void method(Object obj){
if(obj instanceof Reference){
Reference ref = (Reference) obj;
}
}
}

In the main method,

public static void main (String [] args) {  
Object _obj = new Reference();
Reference.method(_obj);
//downcast successfull
}

Why do we need Downcasting really?

Well, you could have declared the reference s1 to be of type Graduate. The main benefit you get by declaring the reference of super type, is the power of polymorphism.

With a super type reference, pointing to a sub class object, you can bind the same reference to multiple sub class objects. And the actual method invoked will be decided at runtime, based on what object is being pointed to. But, the main condition for this is, that method should also be defined in the subclass, else the compiler will fail to find the method declaration.

Here, you were forced to downcast, because you haven't defined the method in the super class. As compiler cannot see the definition of that method in Student class. It has no idea about what the actual object s1 points to. Remember, compiler only checks the reference type to find the meethod declaration.

In general, whenever you see yourself downcasting to a subclass in your code, it is almost always a sign a something wrong (there are some exceptions though). And you should modify your classes.


Let's see what benefit you get by using a super class reference instead of a subclass reference:

For e.g: Suppose you have another sub class of Student as:

class Phd extends Student {
getResearchTopic(){...}
}

and you also provide a definition (a default one) in Student class:

class Student {
getResearchTopic(){...}
}

Now, you create a following two objects, both being pointed to by Student reference:

Student student = new Phd();
student.getResearchTopic(); // Calls Phd class method

student = new Graduate();
student.getResearchTopic(); // Calls Graduate class method

So, with only a single reference, you get to access methods specific to subclasses.


One major implementation of this feature you can see in factory method pattern, where a single static method returns an object of different sub classes based on some condition:

public static Student getInstance(String type) {
if (type.equals("graduate"))
return new Graduate();
else if (type.equals("phd"))
return new Phd();
}

So, you can see that the same method returns an object of different subclasses.

All of the above stuffs you can do just because of one concept:

A Super class reference can refer to any sub class objects, but not vice-versa.

Upcasting/Downcasting in Java

With the implicit upcast at this line:

Animal myAnimal = myDog;

You are not doing anything to change the underlying instance myDog. What you are doing is assigning it to a variable of a type one level higher in the inheritance tree. Effectively, this restricts which methods can be called to only those defined in Animal, but does not change how those methods resolve.

Because you have restricted the methods available to only those defined on the parent class Animal, the compiler cannot resolve Dog#bark(), since it is a method of Dog, and the variable myAnimal is defined to be of type Animal which has no #bark method.

#move() is a method of both Animal and Dog, so it resolves, but it resolves to the method defined on Dog, since myAnimal still refers to an instance of Dog, despite being upcast.

Upcasting and Downcasting in java

Downcasting is a necessary evil, for example when dealing with legacy APIs that return non-generic collections. Another classic example is an equals method:

public class Phleem{

public Phleem(final String phloom){
if(phloom == null){
throw new NullPointerException();
}
this.phloom = phloom;
}

private final String phloom;

public String getPhloom(){
return phloom;
}

@Override
public boolean equals(final Object obj){
if(obj instanceof Phleem){
// downcast here
final Phleem other = (Phleem) obj;
return other.phloom.equals(phloom);
}
return false;
}

// ...

}

I can't think of an example where Upcasting is necessary though. OK, it's good practice to return the least specific possible Object from a method, but that can be done entirely without casting:

public Collection<String> doStuff(){
// no casting needed
return new LinkedHashSet<String>();
}

In java, does repetition of downcasting and upcasting erases data inside subclass?

In Java, object types are reference types, meaning that an expression never actually evaluates to an object, only to a reference to an object.

As a result, upcasting and downcasting don't actually touch the object at all; they just give you references of different types to the object.

That is, this Java code:

(Subclass)((Superclass) instanceOfSubclass)

is roughly analogous to this C++ code:

dynamic_cast<subclass &>(static_cast<superclass &>(instance_of_subclass))

However, if you do conversions with value types such as int and float, then you can indeed lose data; this Java code:

(double)((int) 3.5)

evaluates to 3 rather than to 3.5, because the conversion to type int discarded the fractional part of the value. That's analogous to this C++ code:

static_cast<double>(static_cast<int>(3.5))


Related Topics



Leave a reply



Submit