Java Inheritance and Polymorphism

JavaJavaBeginner
Practice Now

Introduction

In this lab, we'll explore two fundamental concepts of object-oriented programming in Java: inheritance and polymorphism. These powerful features allow us to create more organized, efficient, and flexible code. We'll start by exploring inheritance, which enables us to create new classes based on existing ones, and then move on to polymorphism, which allows us to treat objects of different classes in a uniform way.

By the end of this lab, you'll be able to:

  1. Create class hierarchies using inheritance
  2. Override methods in subclasses
  3. Understand and use polymorphism
  4. Implement abstract classes and methods

Don't worry if these terms sound complex – we'll break everything down into simple, easy-to-follow steps. Let's begin our exciting journey to enhance your Java programming skills!


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL java(("`Java`")) -.-> java/ProgrammingTechniquesGroup(["`Programming Techniques`"]) java(("`Java`")) -.-> java/ObjectOrientedandAdvancedConceptsGroup(["`Object-Oriented and Advanced Concepts`"]) java(("`Java`")) -.-> java/DataStructuresGroup(["`Data Structures`"]) java/ProgrammingTechniquesGroup -.-> java/method_overriding("`Method Overriding`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/annotation("`Annotation`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/classes_objects("`Classes/Objects`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/class_attributes("`Class Attributes`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/class_methods("`Class Methods`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/inheritance("`Inheritance`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/oop("`OOP`") java/ObjectOrientedandAdvancedConceptsGroup -.-> java/polymorphism("`Polymorphism`") java/DataStructuresGroup -.-> java/arrays("`Arrays`") subgraph Lab Skills java/method_overriding -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/annotation -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/classes_objects -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/class_attributes -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/class_methods -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/inheritance -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/oop -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/polymorphism -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} java/arrays -.-> lab-413825{{"`Java Inheritance and Polymorphism`"}} end

Creating a Base Class

We'll start by creating a base class called Animal. This class will serve as the foundation for our other classes.

  1. Open your terminal and navigate to your project directory:

    cd ~/project
  2. Create a new file called Animal.java using the touch command:

    touch Animal.java
  3. Open Animal.java in your text editor and add the following code:

    public class Animal {
        private String name;
        private int age;
    
        public Animal(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public void eat() {
            System.out.println(name + " is eating.");
        }
    
        public void sleep() {
            System.out.println(name + " is sleeping.");
        }
    
        public String getName() {
            return name;
        }
    
        public int getAge() {
            return age;
        }
    }

    Let's break down this code:

    • We define a class named Animal with two attributes: name (a String) and age (an int).
    • The private keyword means these attributes can only be accessed within the class.
    • We have a constructor that initializes these attributes when an Animal object is created.
    • We have two methods: eat() and sleep(), which print what the animal is doing.
    • We also have "getter" methods (getName() and getAge()) that allow us to access the private attributes from outside the class.
  4. Save the Animal.java file.

    alt text
  5. Now, let's compile our Animal class to make sure there are no errors. In your terminal, run:

    javac Animal.java

    If there are no error messages, your class compiled successfully!

Creating a Subclass

Now that we have our base Animal class, let's create a subclass called Dog. This will demonstrate how inheritance works in Java.

  1. In your terminal, create a new file called Dog.java:

    touch Dog.java
  2. Open Dog.java in your text editor and add the following code:

    public class Dog extends Animal {
        private String breed;
    
        public Dog(String name, int age, String breed) {
            super(name, age);  // Call the superclass constructor
            this.breed = breed;
        }
    
        public String getBreed() {
            return breed;
        }
    }

    Let's break down this new code:

    • extends Animal tells Java that Dog is a subclass of Animal. This means Dog inherits all the non-private methods and attributes from Animal.
    • We've added a new attribute breed that's specific to Dog.
    • The constructor takes three parameters. It uses super(name, age) to call the Animal constructor, then sets the breed.
    • We've added a new method getBreed() that's specific to Dog.
  3. Save the Dog.java file.

  4. Compile the Dog class:

    javac Dog.java

    You might see a warning about Animal.class, but that's okay for now.

Demonstrating Inheritance

Now that we have our Animal and Dog classes, let's create a program to demonstrate how inheritance works.

  1. Create a new file called InheritanceDemo.java:

    touch InheritanceDemo.java
  2. Open InheritanceDemo.java and add the following code:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal genericAnimal = new Animal("Generic Animal", 5);
            Dog myDog = new Dog("Buddy", 3, "Labrador");
    
            System.out.println("Demonstrating Animal class:");
            genericAnimal.eat();
            genericAnimal.sleep();
    
            System.out.println("\nDemonstrating Dog class:");
            myDog.eat();  // Inherited from Animal
            myDog.sleep();  // Inherited from Animal
    
            System.out.println("\nDog details:");
            System.out.println("Name: " + myDog.getName());  // Inherited method
            System.out.println("Age: " + myDog.getAge());  // Inherited method
            System.out.println("Breed: " + myDog.getBreed());  // Dog-specific method
        }
    }

    This program creates instances of both Animal and Dog classes and demonstrates how the Dog class inherits methods from the Animal class.

  3. Save the InheritanceDemo.java file.

  4. Compile and run the program:

    javac InheritanceDemo.java
    java InheritanceDemo

    You should see output similar to this:

    Demonstrating Animal class:
    Generic Animal is eating.
    Generic Animal is sleeping.
    
    Demonstrating Dog class:
    Buddy is eating.
    Buddy is sleeping.
    
    Dog details:
    Name: Buddy
    Age: 3
    Breed: Labrador

This demonstration shows how the Dog class inherits attributes and methods from the Animal class, while also adding its own specific attribute (breed) and method (getBreed()).

Method Overriding

Method overriding is a feature that allows a subclass to provide a specific implementation of a method that is already defined in its superclass. Let's see how this works.

  1. Open Dog.java and add the following method:

    @Override
    public void eat() {
        System.out.println(getName() + " is eating dog food.");
    }

    Add this method inside the Dog class, but outside of any other methods.

    The @Override annotation tells the compiler that we intend to override a method from the superclass. It's not required, but it's good practice to use it.

  2. Save the Dog.java file.

  3. Now, let's modify our InheritanceDemo.java to demonstrate method overriding. Open InheritanceDemo.java and replace its content with:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal genericAnimal = new Animal("Generic Animal", 5);
            Dog myDog = new Dog("Buddy", 3, "Labrador");
    
            System.out.println("Demonstrating method overriding:");
            genericAnimal.eat();
            myDog.eat();
    
            System.out.println("\nDemonstrating inherited method:");
            myDog.sleep();  // This method is still inherited from Animal
    
            System.out.println("\nDog details:");
            System.out.println("Name: " + myDog.getName());
            System.out.println("Age: " + myDog.getAge());
            System.out.println("Breed: " + myDog.getBreed());
        }
    }
  4. Save the InheritanceDemo.java file.

  5. Compile and run the updated program:

    javac Animal.java Dog.java InheritanceDemo.java
    java InheritanceDemo
    alt text

    You should see output similar to this:

    Demonstrating method overriding:
    Generic Animal is eating.
    Buddy is eating dog food.
    
    Demonstrating inherited method:
    Buddy is sleeping.
    
    Dog details:
    Name: Buddy
    Age: 3
    Breed: Labrador

This demonstrates how method overriding allows the Dog class to provide its own implementation of the eat() method, while still inheriting other methods like sleep() from the Animal class.

Introduction to Polymorphism

Polymorphism is a fundamental concept in object-oriented programming that allows us to use a base class reference to refer to a subclass object. This enables more flexible and reusable code. Let's see how it works.

  1. Create a new file called Cat.java:

    touch Cat.java
  2. Open Cat.java and add the following code:

    public class Cat extends Animal {
        public Cat(String name, int age) {
            super(name, age);
        }
    
        @Override
        public void eat() {
            System.out.println(getName() + " is eating fish.");
        }
    
        public void meow() {
            System.out.println(getName() + " says: Meow!");
        }
    }

    This creates another subclass of Animal with its own eat() method and a new meow() method.

  3. Save the Cat.java file.

  4. Now, let's update our InheritanceDemo.java to demonstrate polymorphism. Replace its content with:

    public class InheritanceDemo {
        public static void main(String[] args) {
            Animal[] animals = new Animal[3];
            animals[0] = new Animal("Generic Animal", 5);
            animals[1] = new Dog("Buddy", 3, "Labrador");
            animals[2] = new Cat("Whiskers", 2);
    
            System.out.println("Demonstrating polymorphism:");
            for (Animal animal : animals) {
                animal.eat();  // This will call the appropriate eat() method for each animal
            }
    
            System.out.println("\nAccessing specific methods:");
            ((Dog) animals[1]).getBreed();  // We need to cast to Dog to call Dog-specific methods
            ((Cat) animals[2]).meow();      // We need to cast to Cat to call Cat-specific methods
        }
    }

    This code creates an array of Animal objects, but we're actually storing a mix of Animal, Dog, and Cat objects in it. When we call eat() on each animal, Java automatically calls the appropriate version of the method based on the actual type of the object.

  5. Save the InheritanceDemo.java file.

  6. Compile and run the updated program:

    javac Animal.java Dog.java Cat.java InheritanceDemo.java
    java InheritanceDemo

    You should see output similar to this:

    Demonstrating polymorphism:
    Generic Animal is eating.
    Buddy is eating dog food.
    Whiskers is eating fish.
    
    Accessing specific methods:
    Whiskers says: Meow!

This demonstrates polymorphism in action. We're able to treat all the objects as Animal objects, but when we call the eat() method, each object behaves according to its specific class implementation.

Summary

In this lab, we've explored some key concepts of object-oriented programming in Java:

  1. Inheritance: We created a base Animal class and derived Dog and Cat classes from it. This allowed us to reuse code and create a logical hierarchy of classes.
  2. Method Overriding: We saw how subclasses can provide their own implementations of methods defined in the superclass, allowing for more specific behavior.
  3. Polymorphism: We learned how to treat objects of different classes uniformly through their common superclass, enabling more flexible and reusable code.

These concepts are fundamental to Java and object-oriented programming in general. They allow us to create more organized, efficient, and flexible code structures. As you continue your Java journey, you'll find these concepts used extensively in more complex applications.

Remember, practice is key to mastering these concepts.

Other Java Tutorials you may like