Object Type Casting is a very important and tricky concept in Java. It allows the objects of subclasses to be treated as objects of their corresponding superclass, promoting code reusability and flexibility. Performing typecasting means accessing the subclass-specific methods and fields that wouldn’t be available through the superclass interface alone. This can be done in both directions like accessing the specific methods of superclass by the sub-class object.

This guide explains the concept of Object Type Casting in Java.

What is Object Type Casting in Java?

The Object Type Casting is done in two directions: “Upcasting(Widening)” and “Downcasting(Narrowing)”. Let’s implement the Java program to understand more about the Object Type Casting concept.

Widening Or Upcasting The Object Type

In Upcasting or Widening, the cast of the subclass is modified so that it can interact with the parent class’ specific methods and fields. The user must ensure that the subclass is the instance of the targeted parent class or else the “TypeCastingException” will appear. Let’s proceed to the Java code:

class Birds{
  protected String callSign;
  protected int age;
  public Birds(String name, int age){
  this.callSign = name;
  this.age = age;
  }
  public void birdsInfo() {
  System.out.println("\nParent Birds class -> ");
  System.out.println("The Caller Sign is -> " + this.callSign);
  System.out.println("Age -> " + this.age);
  }
}
public class birdChild extends Birds {
  public String color;
  public birdChild(String callSign, int age, String color){
  super(callSign, age);
  this.color = color;
  }
  public void hawkInfo() {
  System.out.println("\nChild birdChild Class => ");
  System.out.println("The Call Sign is => " + this.callSign);
  System.out.println("Age=>  " + this.age);
  System.out.println("With the Color of => " + this.color);
  }
  public static void main(String[] args) {
  birdChild hawk = new birdChild("Junior", 2, "Brown");
  Birds birds = new Birds("Strike", 1);

  birds = hawk;
  birds.birdsInfo();
  }
}

The working of the above code is as follows:

  • First, create a parent class “Birds” accepting and assigning values to two variables via the default constructor. Display the assigned values over the console via the”birdsInfo()” named custom function.
  • Now, create another class “birdChild” extending the “Birds” class. It accepts three values from which pass the two values towards the parent “Birds” class and assigns the third value to a local variable. It also contains a function “hawkInfo()” that displays the class-specific variable values.
  • Now, define the main() method and create an instance for the both “Birds” and “birdChild” classes. Also, pass the values in their constructors.
  • Finally, perform the upcasting by assigning the reference of the child object to the parent object. This widens the cast of the parent object storing the reference of the child class and using this object invokes the Parent class methods. 

Output:

Narrowing or Downcasting the Object Type in Java

Downcasting is the opposite of “Upcasting” as it downcasts the parent class object to access the methods or fields that are specific to the child class. Moreover, it is not done automatically and the developer must explicitly perform the Narrowing type casting:

class Birds{
  protected String name;
  protected int age;
  public Birds(String name, int age){
    this.name = name;
    this.age = age;
  }
  public void birdsInfo() {
  System.out.println("\n\tBirds class info:  ");
  System.out.println(" Name =>  " + this.name);
  System.out.println(" Age =>  " + this.age);
  }
}
public class birdChild extends Birds {
  public String color;
  public birdChild(String name, int age, String color){
  super(name, age);
  this.color = color;
  }

  public void hawkInfo() {
  System.out.println("\n\tbirdChild class: ");
  System.out.println(" Name =>  " + this.name);
  System.out.println(" Age =>  " + this.age);
  System.out.println(" Color =>  " + this.color);
  }

  public static void main(String[] args) {
  Birds birds = new birdChild("Leo", 2, "Black");
 
  birdChild hawk = (birdChild) birds; //Downcasting
  hawk.hawkInfo();    
  }
}

In the above code:

  • First, create the parent “Birds” and child “birdsChild” class along with their properties just like done in the above example.
  • Inside the main() method create an instance “birds” for the child class with the reference of the parent “Birds” class. This prevents the chance of raising the “runtime exception”.
  • Next, perform the downcasting by passing the reference of the “birdsChild” class to the “birds” class. 
  • Lastly, use this narrowed cast parent object to invoke the child class method.

Output:

Downcasting Using Cast() Method in Java

The “cast()” method is provided by Java to directly perform the downcasting over the class provided by the “class” object. For better understanding, let’s consider the same program to downcast and retrieve the class name after downcasting: 

class Birds{
  protected String name;
  protected int age;
  public Birds(String name, int age){
  this.name = name;
  this.age = age;
  }

  public void birdsInfo() {
  System.out.println("\n\tBirds class info:  ");
  System.out.println(" Name=>  " + this.name);
  System.out.println(" Age=>  " + this.age);
  }
}
class Child extends Birds {
  public String color;
  public Child(String name, int age, String color){
  super(name, age);
  this.color = color;
}

public void hawkInfo() {
  System.out.println("\n\tbirdChild class: ");
  System.out.println(" Name=>  "+this.name);
  System.out.println(" Age=>  "+this.age);
  System.out.println(" Color=>  "+this.color);
}
}

public class birdChild {
public static void main(String[] args) {

  Birds birds = new Birds("Strike", 2);
  Child hawk = new Child("Junior", 1, "Brown");

  System.out.println("\n" + birds.getClass());
  birds = Birds.class.cast(hawk);
  System.out.println("\n" + hawk.getClass());

  System.out.println("\n" + birds.getClass());
}
}

The above code works like this:

  • First, create the “Bird” and “Child” classes containing class members and method functions just like the above examples.
  • Then, move inside the “main()” method and create instances of both classes namely “birds” and “hawk”.
  • Now, apply the “getClass()” method over the parent “birds” class object to retrieve the class name.
  • After that, call the “class.cast()” method over the “Birds” class and pass the “hawk” object as an argument. This will downcast the “Birds” class object into the object of a “Child” class like “hawk”.
  • Lastly, retrieve and display the class names of both “birds” and “hawk” classes to confirm the typecasting.

Output

Handling the ClassCastException

While performing “downcasting” there is a high chance of getting “ClassCastException”. This exception is raised when the user tries to cast an object to a type that is not its instance. This exception appears like this:

To handle this exception, utilize the “try/catch” block and the “instanceof” property. This property will first check whether the child class is an instance of the parent class or not and perform downcasting only in the case of “true”:

public static void main(String[] args) {
  Birds birds = new birdChild("Leo", 2, "Black");
  birdChild hawk = new birdChild("Strike", 2, "Brown");
  if (hawk instanceof birdChild) {
    hawk = (birdChild) birds;
    hawk.birdsInfo();
    hawk.hawkInfo();
  }
  else {
    System.out.println("Can't Downcast to Hawk as it's not an Instance of Birds.");
  }
  }

Conclusion

The “Object Type Casting” is done to modify the default scope of class objects. This modification allows the objects of subclasses to act as objects of their corresponding superclass known as Upcasting. In addition, they allow the superclass objects to be treated as the object of their inherited Child class. This process is called Downcasting or Narrowing. Both of these processes are casting types and promote the code’s reusability and flexibility.