/ Backend Development

Reference Casting in Java

Creator Image
Bashar Alshaibani
16 May 2024 -
8 min Reading time

In Java, reference casting allows you to change the type of a reference to match the type of the object it refers to. Understanding when and how to use casting is crucial for managing polymorphism and type safety in your code.

Automatic Casting (Upcasting)

When an object is cast to a superclass or interface type, it is called upcasting. Upcasting is always safe and does not require an explicit cast because it involves casting to a more general type.

Example of Upcasting:

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }

    void fetch() {
        System.out.println("Dog fetches");
    }
}

public class TestUpcasting {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Animal animal = dog; // Automatic upcasting
        animal.makeSound(); // Outputs "Dog barks" because of polymorphism
    }
}

Explicit Casting (Downcasting)

When you cast a reference to a more specific type (subclass), it is called downcasting. Downcasting requires an explicit cast because it involves narrowing the reference to a more specific type. If the actual object is not an instance of the specified subclass, a ClassCastException will be thrown at runtime.

Example of Downcasting:

class Animal {
    void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("Dog barks");
    }

    void fetch() {
        System.out.println("Dog fetches");
    }
}

public class TestDowncasting {
    public static void main(String[] args) {
        Animal animal = new Dog(); // Upcasting
        Dog dog = (Dog) animal;    // Explicit downcasting
        dog.makeSound(); // Outputs "Dog barks"
        dog.fetch();     // Outputs "Dog fetches"
        
        // Unsafe downcasting that will cause a ClassCastException at runtime
        Animal anotherAnimal = new Animal();
        Dog anotherDog = (Dog) anotherAnimal; // Will throw ClassCastException
        anotherDog.makeSound(); // This line will not be reached
    }
}

Valid and Invalid Casts

  1. Valid Casts:

    • Upcasting: From a subclass to a superclass or from an implementing class to an interface.
    • Downcasting: From a superclass to a subclass or from an interface to an implementing class, provided the actual object type is compatible.
  2. Invalid Casts:

    • Between unrelated types: The compiler will not allow casting between unrelated classes.
    • Unsafe downcasting: If the actual object is not of the target type, a ClassCastException will occur at runtime.

Recognizing Compiler-Time and Runtime Errors

  • Compiler-Time Errors: The Java compiler will catch casting errors between unrelated types because it can determine the type hierarchy at compile time.

    Animal animal = new Animal();
    String str = (String) animal; // Compiler error: Inconvertible types
    
  • Runtime Errors: The ClassCastException is thrown when the cast is technically legal (from a superclass to a subclass) but fails at runtime because the object is not an instance of the target type.

    Animal animal = new Animal();
    Dog dog = (Dog) animal; // Runtime error: ClassCastException
    

Using the instanceof Operator

To avoid ClassCastException, you can use the instanceof operator to check if the object is an instance of the target type before casting.

Example:

public class TestInstanceOf {
    public static void main(String[] args) {
        Animal animal = new Dog(); // Upcasting

        if (animal instanceof Dog) {
            Dog dog = (Dog) animal; // Safe downcasting
            dog.makeSound(); // Outputs "Dog barks"
            dog.fetch();     // Outputs "Dog fetches"
        } else {
            System.out.println("The object is not an instance of Dog.");
        }

        Animal anotherAnimal = new Animal();

        if (anotherAnimal instanceof Dog) {
            Dog anotherDog = (Dog) anotherAnimal; // Safe downcasting
            anotherDog.makeSound();
        } else {
            System.out.println("The object is not an instance of Dog.");
        }
    }
}

Summary:

  • Upcasting: Automatic and safe. No explicit cast required.
  • Downcasting: Requires explicit cast and is potentially unsafe. Use instanceof to check type compatibility.
  • Compiler-Time Errors: Occur when trying to cast between unrelated types.
  • Runtime Errors: ClassCastException occurs when an invalid downcast is performed.

Understanding these concepts helps ensure type safety and proper usage of polymorphism in Java applications.