Upcasting And Downcasting In Java: Typecasting in Java involves converting one data type into another, which we have seen in our earlier blog post. Within typecasting, we have two special things called “Upcasting” and “Downcasting,” which are like changing one type of object into another.
In Java, objects can be treated similarly to how we handle data types. Objects can be categorized into Parent and Child objects, and this categorization opens up two forms of typecasting: going from Parent to Child or Child to Parent, which we refer to as Upcasting and Downcasting in Java.
In the context of Java, typecasting ensures that functions or operations correctly process variables. Upcasting and Downcasting focuses on transforming a child object into a parent object and vice versa.
Upcasting can be done either implicitly (automatically) or explicitly (manually), but it’s important to note that downcasting must be performed explicitly; it cannot happen automatically. These concepts are valuable tools in Java programming for managing object hierarchies and their interactions.
What is Upcasting And Downcasting In Java?
These terms come into play when there are superclass and subclass relationships. Upcasting involves converting from a subclass to a superclass, while downcasting pertains to converting from a superclass to a subclass. Let’s briefly explain these two types as follows:
Upcasting in Java
Upcasting is treating an object of a subclass (Child Class) as an object of its superclass (Parent Class). It involves converting a reference variable of a subclass to a reference variable of its superclass. Java implicitly does this when there is a relationship between classes through inheritance.
With the help of upcasting, we can access the methods and variables of the parent class, but it’s impossible to access all the methods and variables of the child class using upcasting. Here, we can access only those methods overridden in the child class.
Syntax:
Parent p = new Child();
Let us go through a couple of examples for a better understanding of upcasting in Java:
Upcasting And Downcasting Example 1: In this example, the child class does not override any methods or variables of the parent class.
package com.softwaretestingo.typecasting; // Child Class Not Override any Method Of Parent Class class Animal { void eat() { System.out.println("Animal is eating"); } } class Dog extends Animal { void bark() { System.out.println("Dog is barking"); } } public class UpCastingEx1 { public static void main(String[] args) { // Upcasting Animal animal = new Dog(); // This works, as it's a method from the superclass animal.eat(); // This would result in a compilation error because bark() is not in the Animal class. // animal.bark(); } }
The Output will be:
Animal is eating
In this code, we create a Dog object and upcast it to an Animal. This allows us to access only the methods and fields defined in the Animal class, even though the underlying object is a Dog. Upcasting is safe and doesn’t require explicit casting.
Upcasting And Downcasting Example 2: The child class has overridden methods of the parent class
In this upcasting example where a child class inherits from a parent class in Java, the child class has overridden the methods and variables of the parent class. This is a fundamental concept in Java’s inheritance and polymorphism.
package com.softwaretestingo.typecasting; //Child class has overridden methods of the parent class class Parent { void displayName() { System.out.println("Parent Class Display Name Method."); } } class Child extends Parent { @Override void displayName() { System.out.println("Child Class Display Name Method."); } } public class UpCastingEx2 { public static void main(String[] args) { // Upcasting Parent obj = new Child(); // This works and calls the overridden method in Dog obj.displayName(); } }
The output will be:
Child Class Display Name Method
In this updated example, the displayName() method is defined in both the Parent and Child classes. The @Override annotation in the Child class indicates that we’re overriding the displayName() method from the parent class Parent.
When we upcast a Child object to a Parent, the displayName() method call will invoke the overridden method in the Child class, resulting in the output “Child Class Display Name Method.”
This demonstrates the power of polymorphism in Java, where the actual method implementation to execute is determined at runtime based on the object’s actual type, even though it’s accessed through a reference of its superclass.
Downcasting in Java
On the other hand, downcasting is the opposite of upcasting. Downcasting explicitly converts an object of a superclass to an object of its subclass.
In Java, If you try to create an object of the parent class and assign it to the child class reference variable, then at that time, you will get the compile time error (Type mismatch).
//You Will Get Compile time Type mismatch error Child obj=new parent();
If, by following this process, we do the downcasting, then we will end up with a compile-time Type Miss Match error. That means downcasting is possible in Java, but certain limitations exist. Let us try to find out the limitations:
Scenario 1:
When you do downcast, you must ensure that the object you’re working with is a Child, regardless of the reference variable you’re using. This helps the compiler know what type of object it is when the program runs.
Let us See the code for a better understanding:
Sports obj= new Cricket(); Cricket castedToCricket= (Cricket) obj;
Above in the first statement, we have created an object of the Cricket class and assigned it to the Sports class reference variable. In the second statement, we are explicitly casting and informing the compiler about the object’s runtime.
In the beginning, we created an object of cricket class, and later, we can downcast is possible in this case.
package com.softwaretestingo.typecasting; //Parent Class class Sports { void displayName() { System.out.println("Sports"); } } // Child Class - Cricket class Cricket extends Sports { void displayName() { System.out.println("Cricket"); } } public class DownCastingEx { public static void main(String[] args) { // Downcasting Sports obj=new Cricket(); Cricket castToCricket = (Cricket) obj; castToCricket.displayName(); } }
The Output will be:
Cricket
Scenario 2:
In the above scenario, downcasting is possible, but let us check one more scenario where downcasting is not possible:
package com.softwaretestingo.typecasting; //Parent Class class Flower { void smell() { System.out.println("Flower Class Smell Method"); } } class Rose extends Flower { void smell() { System.out.println("Rose Class Smell Method"); } } public class DownCastingEx2 { public static void main(String[] args) { Flower obj=new Flower(); Rose DowncastObj=(Rose) obj; } }
Flower obj=new Flower(); Rose DowncastObj=(Rose) obj; (OR) Rose DowncastObj=(Rose)new Flower ();
If you perform downcasting directly, then you will get ClassCastException. Because you are attempting to downcast an object of the Flower class to the Rose class, but it’s not a valid downcast in Java.
In Java, downcasting should only be done when the object belongs to the target class or a subclass of the target class. In your code, you have an object obj of the Flower class, and you’re trying to downcast it to Rose.
For this downcast to work without errors, obj must be a Rose object or an object of a class derived from Rose. Otherwise, a ClassCastException will be thrown at runtime because Java doesn’t allow casting to a type the object isn’t an instance of.
Flower obj=new Flower(); Rose DowncastObj=(Rose) obj;
In Simple Word, If we downcast like this, then the obj reference is of Flower type, and it is not an instance of the Rose class nor an instance of the derived Rose class. That’s why we will get Runtime Exception ClassCastException.
Explanation:
- Here, the obj is Flower Type, but the obj is internally pointing to Flower Object. (So the Original Runtime Object type is Flower Type.)
- As per Java Rule, For Downcasting, the original runtime object type(Flower) must be the same as Rose Type or Derived Type Of Rose.
But Flower is not the same as Rose or Derived of Rose. That is why we will get the runtime exception ClassCastException when we try to downcast immediately.
To perform a successful downcast, you need to have an object of the Rose class or a subclass of Rose to cast it to Rose. Here’s an example of a valid downcast:
The Working Code:
package com.softwaretestingo.typecasting; //Parent Class class Flower { void smell() { System.out.println("Flower Class Smell Method"); } } class Rose extends Flower { void color() { System.out.println("Rose Color Is Red"); } } public class DownCastingEx2 { public static void main(String[] args) { // Upcasting Flower obj=new Rose(); //Valid Downcasting Rose DowncastObj=(Rose) obj; // After Downcasting Parent class reference Variable we are accesing Child Class Methods DowncastObj.color(); } }
Downcasting with Instanceof Operator in Java
As in the above example, if the run-time object is not an instance of type cast class or derived class of type cast, we get the runtime exception.
So, by using the instanceof operator, we can validate whether the object is an instanceof of that specific class. Based on the comparison, it will give True if that object is an instanceof of that class; otherwise, we will get False.
package com.softwaretestingo.typecasting; //Parent Class class Flower { void smell() { System.out.println("Flower Class Smell Method"); } } class Rose extends Flower { static void color(Flower objInstance) { if(objInstance instanceof Rose) { System.out.println("obj Is a Instance Of Flower Class"); //Valid Downcasting Rose DowncastObj=(Rose) objInstance; System.out.println("Rose Color Is Red"); } } } public class DownCastingEx2 { public static void main(String[] args) { // Upcasting Flower obj=new Rose(); Rose.color(obj); } }
Output:
obj Is a Instance Of Flower Class Rose Color Is Red
Why do we need upcasting and downcasting?
Upcasting and Downcasting are important when handling object manipulation and class hierarchies. These concepts will help you in abstraction, code reusability, and code flexibility.
We also listed some of the reasons why we need upcasting and downcasting:
- Polymorphism Support: Upcasting facilitates polymorphism by allowing the assignment of derived class objects to base class variables. It enables uniformly treating objects of different derived classes through their shared base class.
- Code Reusability: Upcasting encourages code reusability by enabling methods to accept base class objects as parameters. This means a single method can handle objects of various derived classes, reducing code duplication and promoting reusability.
- Loss of Subclass Methods: When an object is upcasted to a base class, it loses access to its subclass-specific methods. Downcasting becomes essential to regain access to these subclass methods when needed.
- Access to Additional Methods: Downcasting allows access to interface methods not defined in the base class. It allows the utilization of additional methods specific to a derived class, which may not be initially available through the base class reference.
Difference Between Upcasting and Downcasting
These are the following differences between Upcasting and Downcasting:
Aspect | Upcasting | Downcasting |
---|---|---|
Direction | Upcasting moves from a subclass to a superclass. | Downcasting moves from a superclass to a subclass. |
Casting Operation | Implicit, performed automatically by the compiler. | Explicit, requires manual casting using (Subclass) notation. |
Risk of Error | Explicit requires manual casting using (Subclass) notation. | It can be risky and may result in ClassCastException if not used cautiously. |
Access to Methods | It can be risky and may result in ClassCastException if not used cautiously. | Allows access to subclass-specific methods not available in the superclass. |
Polymorphism | Supports polymorphism by treating derived objects as base class objects. | It is not directly related to polymorphism but enables access to subclass-specific behavior. |
Use Case | It is not directly related to polymorphism but enables access to subclass-specific behavior. | You are used when you need to access subclass-specific features or methods. |
Example | Animal animal = new Dog(); (Implicit upcasting) | Dog dog = (Dog) animal; (Explicit downcasting) |
Conclusion:
Understanding the concepts of upcasting and downcasting is essential for effective Java programming. Upcasting allows us to work with objects at a higher level of abstraction, promoting code reusability and polymorphism. On the other hand, downcasting provides access to subclass-specific features and methods when necessary, but it should be used cautiously to avoid runtime errors.
If you have any doubts or questions about “Upcasting and Downcasting in Java,” please don’t hesitate to ask in the comments section below. Your feedback is valuable to us, so if you have any suggestions to improve this article or if there are specific topics you’d like to see covered in more detail, feel free to share your thoughts in the comments. We’re here to help you enhance your Java programming knowledge!