Java Type Casting (Downcasting, Upcasting)
Typecasting is the process of conversion of the type of data. Object Typecasting can be of two types, depending on whether we are converting from parent type to child or going in the other way. In this tutorial, we will learn about Upcasting and Downcasting of objects in Java.
Upcasting
- The process of casting an object of child class to the parent class is called Upcasting.
- Upcasting can be done implicitly or explicitly.
- Upcasting will reduce the choice of methods, we can use on the typecasted object.
- However, for overridden methods, static or dynamic binding comes into play, and it will decide the method implementation to use.
To demonstrate upcasting, we will use a child and a parent class. The child class has an overridden print() method and a printName() method. The following code shows the implementation.
class SuperClass
{
public void print()
{
System.out.println("Printing from SuperClass");
}
}
class Child extends SuperClass
{
String name;
Child(String s)
{
this.name = s;
}
@Override
public void print()
{
System.out.println("Printing from Child class");
}
public void printName()
{
System.out.println(this.name);
}
}
Implicit Upcasting
When we assign an object of the Child class to a reference variable of SuperClass type, then implicit upcasting takes place. We can use the print() method on this object, and the method implementation of the Child class will be used.
public static void main(String[] args)
{
Child c = new Child("child");
c.print();
//Implicit Upcasting
SuperClass s2 = new Child("Second Child");
s2.print();//print() method of Child class is called due to dynamic binding
}
Printing from Child class
Printing from Child class
Another example of implicit upcasting is shown below. If we create an ArrayList
of SuperClass type and add child classes objects to it, then they are implicitly upcasted to the SuperClass. We also created a new class(AnotherChild).
import java.util.ArrayList;
class AnotherChild extends SuperClass
{
public void print()
{
System.out.println("Printing from AnotherChild class");
}
}
public class TypeCastingDemo
{
public static void main(String[] args)
{
SuperClass obj1 = new SuperClass();
Child obj2 = new Child("First Child");
AnotherChild obj3 = new AnotherChild();
ArrayList<SuperClass> superClassList = new ArrayList<>();
superClassList.add(obj1);
superClassList.add(obj2);
superClassList.add(obj3);
for(int i=0; i<superClassList.size(); i++)
superClassList.get(i).print();
}
}
Printing from SuperClass
Printing from Child class
Printing from AnotherChild class
Explicit Upcasting
We can also explicitly cast the object, but it is not really required.
public static void main(String[] args)
{
Child c = new Child("child");
c.print();
//Explicit Upcasting
SuperClass s2 = (SuperClass) new Child("Second Child");
s2.print();//print() method of Child class is called due to dynamic binding
}
Printing from Child class
Printing from Child class
Upcasting Narrows the Choice Of Methods
As discussed above, upcasting will narrow our choices of methods. For example, after upcasting a Child class object to SuperClass, we can no longer use the printName() method of the Child class. We will get a compilation error.
public class TypeCastingDemo
{
public static void main(String[] args)
{
SuperClass s = new Child("First Child");
s.printName();//Error
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method printName() is undefined for the type SuperClass
at TypeCastingDemo.main(TypeCastingDemo.java:36)
Upcasting with Interfaces
Upcasting also works with interfaces. Let's create a new interface and implement it in our Child class.
interface Name
{
public void printName();
}
class Child extends SuperClass implements Name
{
String name;
Child(String s)
{
this.name = s;
}
@Override
public void print()
{
System.out.println("Printing from Child class");
}
public void printName()
{
System.out.println(this.name);
}
}
Now, we will create an object of the Child class and assign it to an interface type variable. Implicit typecasting will take place in this case. We can use the implemented printName() method.
public class TypeCastingDemo
{
public static void main(String[] args)
{
Name obj = new Child("First Child");
obj.printName();
}
}
First Child
But we can't use the overridden print() method.
public class TypeCastingDemo
{
public static void main(String[] args)
{
Name obj = new Child("First Child");
obj.print();//Error
}
}
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The method print() is undefined for the type Name
at TypeCastingDemo.main(TypeCastingDemo.java:51)
Upcasting to Object Class
All classes internally extend the Object class. Object class is the superclass of all other classes. So we can upcast the Child class(or any other class) to the Object class.
public class TypeCastingDemo
{
public static void main(String[] args)
{
Object o = new Child("hello");
System.out.print(o.getClass());
}
}
class Child
Downcasting in Java
- Downcasting can be considered as the reverse of Upcasting. We are typecasting an object of the parent class to the child class.
- Unlike upcasting, downcasting is not done implicitly by the compiler.
As we saw in upcasting, the following code gives an error.
public class TypeCastingDemo
{
public static void main(String[] args)
{
SuperClass s = new Child("First Child");
s.printName();//Error
}
}
To make it work, we can downcast from the SuperClass to the Child class. This will give us access to the print() method of the Child class.
public class TypeCastingDemo
{
public static void main(String[] args)
{
SuperClass s = new Child("First Child");
((Child) s ).printName();//Downcasting from SuperClass to Child class.
}
}
First Child
ClassCastException in TypeCasting
The ClassCastException
is a commonly encountered exception that occurs when downcasting. The following code demonstrates this error.
We created a new AnotherChild class and in the main() method, we created three objects, one for each class(SuperClass class, Child class, and AnotherChild class), and we are adding them to an ArrayList
. Now when we cast each object to the Child class, then we will get this error. This happens because there is no parent-child relationship between the AnotherChild class and the Child class. So we can't downcast from AnotherChild to Child.
class AnotherChild extends SuperClass
{
public void print()
{
System.out.println("Printing from AnotherChild class");
}
}
public class TypeCastingDemo
{
public static void main(String[] args)
{
ArrayList<SuperClass> list = new ArrayList<>();
SuperClass s = new Child("First Child");
Child c1 = new Child("Second Child");
AnotherChild c2 = new AnotherChild();
list.add(s);
list.add(c1);
list.add(c2);
for(int i=0; i<list.size(); i++)
{
((Child) list.get(i)).printName();
}
}
}
First Child
Second Child
Exception in thread "main" java.lang.ClassCastException: class AnotherChild cannot be cast to class Child (AnotherChild and Child are in unnamed module of loader 'app')
at TypeCastingDemo.main(TypeCastingDemo.java:57)
We can use the instanceof operator to rectify this error. This operator returns true if the object is of a particular type. We can correct the above code by using the instanceof operator before downcasting.
public class TypeCastingDemo
{
public static void main(String[] args)
{
ArrayList<SuperClass> list = new ArrayList<>();
SuperClass s = new Child("First Child");
Child c1 = new Child("Second Child");
AnotherChild c2 = new AnotherChild();
list.add(s);
list.add(c1);
list.add(c2);
for(int i=0; i<list.size(); i++)
{
if(list.get(i) instanceof Child)
((Child) list.get(i)).printName();
}
}
}
First Child
Second Child
Using cast() and isInstance() methods
Java provides an alternative way of downcasting. We can use the cast() method to downcast from a superclass to a subclass. Similarly, the isInstance() method can replace the instanceof operator. These two methods are often used together in generic methods. Let's rewrite the above code using these methods.
public class TypeCastingDemo
{
public static void main(String[] args)
{
ArrayList<SuperClass> list = new ArrayList<>();
SuperClass s = new Child("First Child");
Child c1 = new Child("Second Child");
AnotherChild c2 = new AnotherChild();
list.add(s);
list.add(c1);
list.add(c2);
for(int i=0; i<list.size(); i++)
{
if(Child.class.isInstance(list.get(i)))
{
Child c = Child.class.cast(list.get(i));
c.printName();
}
}
}
}
First Child
Second Child
Summary
In this tutorial, we learned how to upcast and downcast objects in Java. Upcasting can be done implicitly or explicitly and converts the type from subclass to superclass. It narrows down the methods we can use with the object. Downcasting is the process of casting superclass to subclass. We can only explicitly downcast an object. Downcasting is performed to access the methods and properties of the child class. We must use the instanceof operator or the isInstance() method to downcast safely. It is done to avoid the ClassCastException.