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 ClassCastException
s:
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
How to Get X and Y Index of Element Inside Gridlayout
How to Convert List≪Integer≫ to Int[] in Java
Jcomponents Not Showing Up With Picture Background
What's the Best Way to Build a String of Delimited Items in Java
Unicode Equivalents For \W and \B in Java Regular Expressions
Spring @Transactional Not Auto Rolling Back During Unchecked Exception
Making a Robust, Resizable Swing Chess Gui
How to Measure Time Elapsed in Java
Netbeans Gui Editor Generating Its Own Incomprehensible Code
Error: the Processing Instruction Target Matching "[Xx][Mm][Ll]" Is Not Allowed
String Is Immutable. What Exactly Is the Meaning
Which @Notnull Java Annotation Should I Use