Casting

Introduction

Casting is converting one data type to another. In inheritance, it’s converting between parent and child class references.


Types of Casting

  1. Upcasting (Implicit)
  2. Downcasting (Explicit)

Upcasting

Child to parent conversion (automatic).

Syntax:

Parent p = new Child();  // Implicit upcasting

Example:

class Animal {
    void eat() {
        System.out.println("Eating");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Barking");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();

        // Upcasting (implicit)
        Animal animal = dog;

        animal.eat();   // ✓ OK
        // animal.bark();  // ✗ Error: Animal reference can't access Dog methods
    }
}

Downcasting

Parent to child conversion (explicit cast required).

Syntax:

Child c = (Child) parentReference;

Example:

class Animal {
    void eat() {
        System.out.println("Eating");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("Barking");
    }
}

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

        // Downcasting (explicit)
        Dog dog = (Dog) animal;

        dog.eat();   // ✓ OK
        dog.bark();  // ✓ OK now
    }
}

Why Casting?

class Shape {
    void draw() {
        System.out.println("Drawing shape");
    }
}

class Circle extends Shape {
    double radius;

    void draw() {
        System.out.println("Drawing circle");
    }

    double area() {
        return Math.PI * radius * radius;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape shape = new Circle();  // Upcasting
        shape.draw();  // Works (polymorphism)

        // shape.area();  // ✗ Error: Shape doesn't have area()

        // Need downcasting to access Circle-specific methods
        Circle circle = (Circle) shape;
        System.out.println("Area: " + circle.area());  // ✓ OK
    }
}

ClassCastException

Runtime error when invalid cast.

class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }

public class Main {
    public static void main(String[] args) {
        Animal animal = new Cat();  // Cat object

        // Dog dog = (Dog) animal;  // ✗ Runtime Error: ClassCastException
        // Cannot cast Cat to Dog
    }
}

instanceof Operator

Check type before casting to avoid errors.

Syntax:

if (object instanceof ClassName) {
    // Safe to cast
}

Example:

class Animal { }
class Dog extends Animal {
    void bark() {
        System.out.println("Bark");
    }
}
class Cat extends Animal {
    void meow() {
        System.out.println("Meow");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat(), new Dog()};

        for (Animal animal : animals) {
            if (animal instanceof Dog) {
                Dog dog = (Dog) animal;
                dog.bark();
            } else if (animal instanceof Cat) {
                Cat cat = (Cat) animal;
                cat.meow();
            }
        }
    }
}

Complete Example

class Employee {
    String name;
    double salary;

    Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    void work() {
        System.out.println(name + " is working");
    }
}

class Manager extends Employee {
    String department;

    Manager(String name, double salary, String department) {
        super(name, salary);
        this.department = department;
    }

    void manage() {
        System.out.println(name + " manages " + department);
    }
}

class Developer extends Employee {
    String language;

    Developer(String name, double salary, String language) {
        super(name, salary);
        this.language = language;
    }

    void code() {
        System.out.println(name + " codes in " + language);
    }
}

public class Main {
    public static void main(String[] args) {
        Employee[] employees = new Employee[3];
        employees[0] = new Employee("John", 30000);
        employees[1] = new Manager("Alice", 50000, "IT");
        employees[2] = new Developer("Bob", 40000, "Java");

        for (Employee emp : employees) {
            emp.work();  // Polymorphic call

            // Type-specific operations
            if (emp instanceof Manager) {
                Manager mgr = (Manager) emp;  // Downcast
                mgr.manage();
            } else if (emp instanceof Developer) {
                Developer dev = (Developer) emp;  // Downcast
                dev.code();
            }

            System.out.println();
        }
    }
}

Casting Rules

Valid Casts:

class A { }
class B extends A { }
class C extends B { }

// Upcasting (always safe)
A a = new B();  // ✓
A a = new C();  // ✓
B b = new C();  // ✓

// Downcasting (needs check)
A a = new C();
if (a instanceof C) {
    C c = (C) a;  // ✓ Safe
}

Invalid Casts:

class Dog { }
class Cat { }

// Dog dog = (Dog) new Cat();  // ✗ No relationship

Primitive Type Casting

Widening (Implicit):

int i = 10;
double d = i;  // Automatic
System.out.println(d);  // 10.0

Narrowing (Explicit):

double d = 10.5;
int i = (int) d;  // Explicit cast needed
System.out.println(i);  // 10 (loses decimal)

Casting with Methods

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

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }

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

class AnimalHandler {
    static void handleAnimal(Animal animal) {
        animal.sound();  // Polymorphic

        // Access Dog-specific method
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;
            dog.fetch();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        AnimalHandler.handleAnimal(new Dog());
    }
}

Multiple Levels

class A { }
class B extends A { }
class C extends B { }

public class Main {
    public static void main(String[] args) {
        A a = new C();  // Upcasting C -> A

        // Downcast to B
        if (a instanceof B) {
            B b = (B) a;
            System.out.println("Cast to B successful");
        }

        // Downcast to C
        if (a instanceof C) {
            C c = (C) a;
            System.out.println("Cast to C successful");
        }
    }
}

Safe Casting Pattern

public class SafeCasting {
    static void processShape(Object obj) {
        if (obj instanceof Circle) {
            Circle circle = (Circle) obj;
            System.out.println("Circle area: " + circle.area());
        } else if (obj instanceof Rectangle) {
            Rectangle rect = (Rectangle) obj;
            System.out.println("Rectangle area: " + rect.area());
        } else {
            System.out.println("Unknown shape");
        }
    }
}

Common Mistakes

Mistake 1: Casting Unrelated Classes

class Dog { }
class Cat { }

// Dog dog = (Dog) new Cat();  // ✗ Compile error

Mistake 2: Not Checking Before Downcast

Animal animal = new Cat();
// Dog dog = (Dog) animal;  // ✗ Runtime error

// ✓ Correct
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}

Mistake 3: Unnecessary Casting

Dog dog = new Dog();
Animal animal = dog;       // ✓ No cast needed (upcasting)
Dog d = (Dog) animal;      // Cast needed (downcasting)

Real-World Example: Collections

import java.util.ArrayList;

class Vehicle { }
class Car extends Vehicle {
    void drive() {
        System.out.println("Driving car");
    }
}
class Bike extends Vehicle {
    void ride() {
        System.out.println("Riding bike");
    }
}

public class Main {
    public static void main(String[] args) {
        ArrayList<Vehicle> vehicles = new ArrayList<>();
        vehicles.add(new Car());   // Upcasting
        vehicles.add(new Bike());  // Upcasting

        for (Vehicle v : vehicles) {
            if (v instanceof Car) {
                Car car = (Car) v;  // Downcasting
                car.drive();
            } else if (v instanceof Bike) {
                Bike bike = (Bike) v;  // Downcasting
                bike.ride();
            }
        }
    }
}

Comparison Table

FeatureUpcastingDowncasting
DirectionChild → ParentParent → Child
SyntaxImplicitExplicit cast
SafetyAlways safeMay fail
Check neededNoYes (instanceof)
ExampleParent p = new Child()Child c = (Child) p
ErrorNeverClassCastException

Pattern Matching (Java 16+)

// Modern Java (16+)
if (animal instanceof Dog dog) {
    // dog variable automatically created
    dog.bark();
}

// Old way
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    dog.bark();
}

Quick Reference

// Upcasting (implicit)
Parent p = new Child();

// Downcasting (explicit)
Child c = (Child) p;

// Safe downcasting
if (p instanceof Child) {
    Child c = (Child) p;
    // Use c
}

// Check multiple types
if (obj instanceof Dog) {
    ((Dog) obj).bark();
} else if (obj instanceof Cat) {
    ((Cat) obj).meow();
}

Exam Tips

Remember:

  1. Upcasting = child to parent (automatic)
  2. Downcasting = parent to child (explicit)
  3. Use instanceof before downcasting
  4. ClassCastException at runtime if invalid
  5. Upcasting always safe
  6. Downcasting may fail
  7. Need (ChildType) for downcast
  8. Check type in inheritance hierarchy
  9. No casting between unrelated classes
  10. Enables polymorphism

Common Questions:

  • What is casting?
  • Upcasting vs downcasting?
  • When to use instanceof?
  • What is ClassCastException?
  • Why casting needed?
  • How to safely downcast?
  • Can we cast unrelated classes?
  • Implicit vs explicit casting?