Menu
Coddy logo textTech

Upcasting and Downcasting

Part of the Object Oriented Programming section of Coddy's Java journey — lesson 30 of 87.

In the previous lesson, you saw how a parent-type variable can hold a child object: Animal myPet = new Dog(). This assignment is called upcasting, and it happens automatically because a Dog is always an Animal.

Upcasting moves up the inheritance hierarchy. It's implicit and always safe:

Dog dog = new Dog();
Animal animal = dog;  // Upcasting - automatic

When you upcast, you can only access methods defined in the parent class. The child-specific methods become hidden, even though the object is still a Dog underneath.

Downcasting goes the opposite direction, converting a parent reference back to a child type. This requires an explicit cast because it's not always safe:

Animal animal = new Dog();  // Upcasting
Dog dog = (Dog) animal;     // Downcasting - explicit cast required
dog.bark();                 // Now we can access Dog methods

Downcasting is risky. If the object isn't actually the type you're casting to, Java throws a ClassCastException at runtime:

Animal animal = new Cat();
Dog dog = (Dog) animal;  // Compiles, but crashes at runtime!

The compiler can't catch this error because it only sees the declared types. The actual object type is only known when the program runs. In the next lesson, you'll learn how to safely check an object's type before downcasting.

challenge icon

Challenge

Easy

Let's build a media player system that demonstrates both upcasting and downcasting in action. You'll create a hierarchy of media types and practice converting between parent and child references.

You'll organize your code across four files:

  • Media.java: Create the base class that all media types share. Every media item has a title field (String). Include a constructor to initialize it, a getter method getTitle(), and a method play() that prints: Playing: [title]
  • Song.java: Create a class that extends Media. Songs have an additional artist field (String). Use super for the parent initialization. Override play() to print: Playing song: [title] by [artist]. Add a method showArtist() that prints: Artist: [artist]
  • Podcast.java: Create another class that extends Media. Podcasts have an episode field (int). Override play() to print: Playing podcast: [title] - Episode [episode]. Add a method showEpisode() that prints: Episode: [episode]
  • Main.java: Demonstrate upcasting and downcasting with your media types. You'll receive four inputs: a song title, an artist name, a podcast title, and an episode number.

    First, create a Song and upcast it to a Media reference. Call play() on this reference to see run-time polymorphism at work.

    Next, downcast that Media reference back to a Song and call showArtist() to access the child-specific method.

    Then, create a Podcast and upcast it to a Media reference. Call play() on this reference.

    Finally, downcast that Media reference back to a Podcast and call showEpisode().

You will receive four inputs: the song title (String), artist name (String), podcast title (String), and episode number (int).

Your output should show four lines - two for the song (its play output and artist info) and two for the podcast (its play output and episode info). Notice how upcasting lets you treat different media types uniformly, while downcasting lets you access their unique features!

Cheat sheet

In Java, upcasting moves up the inheritance hierarchy and happens automatically. A child object can be assigned to a parent-type variable:

Dog dog = new Dog();
Animal animal = dog;  // Upcasting - automatic

When you upcast, you can only access methods defined in the parent class, even though the object is still the child type underneath.

Downcasting converts a parent reference back to a child type. It requires an explicit cast:

Animal animal = new Dog();  // Upcasting
Dog dog = (Dog) animal;     // Downcasting - explicit cast required
dog.bark();                 // Now we can access Dog methods

Downcasting is risky. If the object isn't actually the type you're casting to, Java throws a ClassCastException at runtime:

Animal animal = new Cat();
Dog dog = (Dog) animal;  // Compiles, but crashes at runtime!

The compiler can't catch this error because it only sees the declared types. The actual object type is only known when the program runs.

Try it yourself

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Read inputs
        String songTitle = scanner.nextLine();
        String artistName = scanner.nextLine();
        String podcastTitle = scanner.nextLine();
        int episodeNumber = scanner.nextInt();
        
        // TODO: Create a Song and upcast it to a Media reference
        
        // TODO: Call play() on the Media reference (demonstrates polymorphism)
        
        // TODO: Downcast the Media reference back to Song
        
        // TODO: Call showArtist() on the Song reference
        
        // TODO: Create a Podcast and upcast it to a Media reference
        
        // TODO: Call play() on the Media reference
        
        // TODO: Downcast the Media reference back to Podcast
        
        // TODO: Call showEpisode() on the Podcast reference
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming