Upcasting & Downcasting in Java with Examples | Full Tutorial
Java is an object-oriented programming language that allows developers to create classes and objects. Upcasting and downcasting are two concepts that are related to class inheritance and object polymorphism.
Upcasting In Java and How Does It work?
It is a process of converting a subclass object to its superclass reference type. In other words, it is a way of treating an object of a subclass as an object of its superclass. This conversion is done implicitly by the Java Virtual Machine (JVM) at runtime.
When upcasting is performed, the subclass object loses its specific attributes and methods that are not present in its superclass. The resulting object can only access the methods and attributes that are defined in the superclass.
Upcasting is useful when you want to use a single reference type to refer to objects of different subclasses. This allows you to write more generic code that can handle different types of objects without having to write separate code for each type.
Example of Upcasting in Java
Let’s consider an example of upcasting in Java. Suppose we have a class hierarchy that looks like this:
// Superclass class Animal { String name = "Generic Animal"; // Instance variable int age = 5; // Instance variable // Constructor Animal(String name, int age) { this.name = name; this.age = age; System.out.println("Animal constructor called"); } void makeSound() { // Overridden method System.out.println("Animal makes a generic sound"); } void eat() { // Overridden method System.out.println("Animal eats food"); } void sleep() { // Not overridden System.out.println("Animal sleeps"); } void move() { // Not overridden System.out.println("Animal moves around"); } void displayInfo() { // Overridden method System.out.println("Animal Name: " + name + ", Age: " + age); } } // Subclass class Dog extends Animal { String name = "Dog"; // Hiding superclass instance variable String breed; // Additional instance variable // Constructor Dog(String name, int age, String breed) { super(name, age); // Call superclass constructor this.breed = breed; System.out.println("Dog constructor called"); } @Override void makeSound() { // Overriding method System.out.println("Dog barks"); } @Override void eat() { // Overriding method super.eat(); // Calling superclass method using 'super' System.out.println("Dog eats bones"); } @Override void displayInfo() { // Overriding method super.displayInfo(); // Call superclass method System.out.println("Breed: " + breed); } void guardHouse() { // Subclass-specific method System.out.println("Dog guards the house"); } } // Main class public class MethodOverridingDemo { public static void main(String[] args) { System.out.println("---- Creating Animal Object ----"); Animal animal1 = new Animal("Wild Animal", 8); animal1.makeSound(); animal1.eat(); animal1.sleep(); animal1.move(); animal1.displayInfo(); System.out.println(); System.out.println("---- Creating Dog Object ----"); Dog dog1 = new Dog("Buddy", 3, "Golden Retriever"); dog1.makeSound(); dog1.eat(); dog1.sleep(); // Not overridden, so calls Animal's method dog1.move(); // Not overridden, so calls Animal's method dog1.displayInfo(); dog1.guardHouse(); System.out.println(); System.out.println("---- Upcasting (Animal reference to Dog object) ----"); Animal animal2 = new Dog("Rocky", 4, "German Shepherd"); animal2.makeSound(); // Calls Dog's overridden method animal2.eat(); // Calls Dog's overridden method animal2.sleep(); // Calls Animal's method (not overridden) animal2.move(); // Calls Animal's method (not overridden) animal2.displayInfo(); // Calls Dog's overridden method System.out.println("Animal name (Upcasting): " + animal2.name); // Access Animal's instance variable System.out.println(); System.out.println("---- Downcasting (Casting back to Dog) ----"); if (animal2 instanceof Dog) { Dog dog2 = (Dog) animal2; System.out.println("Dog name: " + dog2.name); // Access Dog's instance variable dog2.makeSound(); dog2.eat(); dog2.guardHouse(); // Calls Dog-specific method } } }
In this code, we create a Dog object called myDog. We then upcast myDog to an Animal reference called myAnimal. Now, myAnimal can only access the methods and attributes that are defined in the Animal class:
myAnimal.makeSound(); // prints “Dog is barking”
Downcasting in Java and How Does It Work?
Downcasting is the opposite of upcasting. It is a process of converting a superclass reference type to its subclass object. In other words, it is a way of treating an object of a superclass as an object of its subclass. This conversion is done explicitly by the programmer using the cast operator.
When downcasting is performed, the superclass reference is checked to see if it refers to an object of the subclass. If it does, the reference is converted to the subclass type. If it doesn’t, a ClassCastException is thrown at runtime.
Downcasting is useful when you want to access the specific methods and attributes of a subclass object that are not present in its superclass. However, it should be used with caution because it can lead to runtime errors if the superclass reference does not refer to an object of the subclass.
Example of Downcasting in Java
Let’s consider an example of downcasting in Java. Suppose we have the same class hierarchy as before:
class Animal
{
public void makeSound()
{
System.out.println("Animal is making a sound");
}
}
class Dog extends Animal
{
public void makeSound()
{ System.out.println("Dog is barking");
}
public void fetch() {
System.out.println("Dog is fetching");
}
}
// Main class
public class MethodOverridingDemo {
public static void main(String[] args) {
System.out.println("---- Creating Animal Object ----");
Animal animal1 = new Animal("Wild Animal", 8);
animal1.makeSound();
animal1.eat();
animal1.sleep();
animal1.move();
animal1.displayInfo();
System.out.println();
System.out.println("---- Creating Dog Object ----");
Dog dog1 = new Dog("Buddy", 3, "Golden Retriever");
dog1.makeSound();
dog1.eat();
dog1.sleep(); // Not overridden, so calls Animal's method
dog1.move(); // Not overridden, so calls Animal's method
dog1.displayInfo();
dog1.guardHouse();
System.out.println();
System.out.println("---- Upcasting (Animal reference to Dog object) ----");
Animal animal2 = new Dog("Rocky", 4, "German Shepherd");
animal2.makeSound(); // Calls Dog's overridden method
animal2.eat(); // Calls Dog's overridden method
animal2.sleep(); // Calls Animal's method (not overridden)
animal2.move(); // Calls Animal's method (not overridden)
animal2.displayInfo(); // Calls Dog's overridden method
System.out.println("Animal name (Upcasting): " + animal2.name); // Access Animal's instance variable
System.out.println();
System.out.println("---- Downcasting (Casting back to Dog) ----");
if (animal2 instanceof Dog) {
Dog dog2 = (Dog) animal2;
System.out.println("Dog name: " + dog2.name); // Access Dog's instance variable
dog2.makeSound();
dog2.eat();
dog2.guardHouse(); // Calls Dog-specific method
}
}
}
Now, let’s create an Animal object and downcast it to a Dog reference:
Animal myAnimal = new Dog(); Dog myDog = (Dog) myAnimal;
In this code, we create an Animal object called myAnimal. We then downcast myAnimal to a Dog reference called myDog. Now, myDog can access the specific methods and attributes that are defined in the Dog class:
myDog.makeSound(); // prints “Dog is barking” myDog.fetch(); // prints “Dog is fetching”
Difference Between Upcasting and Downcasting
The main difference between upcasting and downcasting is the direction of the conversion. Upcasting converts a subclass object to its superclass reference, while downcasting converts a superclass reference to its subclass object.
Upcasting is done implicitly by the JVM at runtime, while downcasting is done explicitly by the programmer using the cast operator.
It is safe because it only allows access to the methods and attributes that are defined in the superclass. Downcasting, on the other hand, can be dangerous because it can lead to runtime errors if the superclass reference does not refer to an object of the subclass.
Good to Read:- Top Features of JavaScript You Must Know
When to Use Upcasting and Downcasting in Java?
Upcasting is useful when you want to write generic code that can handle different types of objects without having to write separate code for each type. It is also useful when you want to create collections of objects that have a common superclass.
Downcasting is useful when you want to access the specific methods and attributes of a subclass object that are not present in its superclass. However, it should be used with caution because it can lead to runtime errors if the superclass reference does not refer to an object of the subclass.
Best Practices for Upcasting and Downcasting in Java
Here are some best practices for using upcasting and downcasting in Java:
- Use upcasting to write generic code that can handle different types of objects without having to write separate code for each type.
- Use downcasting sparingly and only when you need to access the specific methods and attributes of a subclass object that are not present in its superclass.
- Always check the type of the object before downcasting to avoid runtime errors.
- Avoid using downcasting in performance-critical code because it can be slow.
Common Mistakes to Avoid When Using Upcasting and Downcasting in Java
Here are some common mistakes to avoid when using upcasting and downcasting in Java:
- Using downcasting without checking the type of the object first can lead to runtime errors.
- It excessively can make the code difficult to read and maintain.
- Using upcasting with complex object hierarchies can result in unexpected behavior.
- Forgetting to cast the object before downcasting can also lead to runtime errors.
Conclusion
In conclusion, upcasting and downcasting are two important concepts in Java that allow developers to work with class inheritance and object polymorphism. Upcasting is the process of converting a subclass object to its superclass reference, while downcasting is the process of converting a superclass reference to its subclass object.
Upcasting is useful for writing generic code that can handle different types of objects, while downcasting is useful for accessing the specific methods and attributes of a subclass object that are not present in its superclass. However, downcasting should be used with caution because it can lead to runtime errors.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.