Department of Computer Science
Qarshi University Lahore
 Object Oriented Programming
           (CCL-102)
             Lab
                    Lab 07: Polymorphism in Java
Objective:
● Understand the concept of polymorphism in Java.
● Demonstrate compile-time (method overloading) and runtime (method overriding)
   polymorphism.
● Explore how polymorphism enhances flexibility and reusability.
● Learn to implement polymorphism using superclass references and interfaces.
1. What is Polymorphism?
   Polymorphism is the ability of an object to take on many forms. In Java, it allows
   a single interface to be used for a general class of actions. The most common use
   of polymorphism is when a parent class reference is used to refer to a child class
   object.
   Polymorphism is divided into two types:
   1. Compile-Time Polymorphism (Static Binding)
   - Achieved using method overloading.
   - The method to be executed is determined at compile time.
   2. Runtime Polymorphism (Dynamic Binding)
   - Achieved using method overriding.
   - The method to be executed is determined at runtime depending on the object.
   Advantages of Polymorphism:
   - Increases code reusability.
   - Supports method overriding for dynamic behavior.
   - Reduces code complexity.
   - Enables interface-driven development.
2. Syntax of Overloading and Overriding
   Method Overloading:
   class ClassName {
      returnType methodName(parameter1) {
         // logic
      }
      returnType methodName(parameter1, parameter2) {
         // logic
      }
   }
   Method Overriding:
   class SuperClass {
      void display() {
         System.out.println("Display from SuperClass");
      }
   }
   class SubClass extends SuperClass {
      @Override
      void display() {
        System.out.println("Display from SubClass");
      }
   }
3. Example of Polymorphism
   ////////// Overloading Example ////////////////////
   public class Calculator {
     // Method with two integer parameters
     public int add(int a, int b) {
        System.out.println("Adding two integers");
        return a + b;
     }
       // Method with three integer parameters
       public int add(int a, int b, int c) {
          System.out.println("Adding three integers");
          return a + b + c;
       }
       // Method with two double parameters
       public double add(double a, double b) {
          System.out.println("Adding two doubles");
          return a + b;
       }
       public static void main(String[] args) {
         Calculator calc = new Calculator();
           System.out.println(calc.add(5, 10));          // Calls the first method
           System.out.println(calc.add(5, 10, 15));        // Calls the second method
           System.out.println(calc.add(5.5, 10.5));        // Calls the third method
       }
   }
   ////// Overriding Example ///////////////////
   // Parent class
   class Animal {
    public void makeSound() {
      System.out.println("Animal makes a sound");
    }
    public void eat() {
      System.out.println("Animal eats food");
    }
}
// Subclass overriding methods from parent class
class Dog extends Animal {
   @Override
   public void makeSound() {
     System.out.println("Dog barks: Woof! Woof!");
   }
    @Override
    public void eat() {
      System.out.println("Dog eats meat");
    }
}
// Another subclass with different overrides
class Cat extends Animal {
   @Override
   public void makeSound() {
     System.out.println("Cat meows: Meow!");
   }
    @Override
    public void eat() {
      System.out.println("Cat eats fish");
    }
}
// Main class to demonstrate runtime polymorphism
public class PolymorphismDemo {
   public static void main(String[] args) {
     // Creating objects of different classes
     Animal myAnimal = new Animal();
     Animal myDog = new Dog(); // Animal reference, Dog object
     Animal myCat = new Cat(); // Animal reference, Cat object
           // Calling methods - the JVM decides which method to call at runtime
           myAnimal.makeSound(); // Output: Animal makes a sound
           myDog.makeSound(); // Output: Dog barks: Woof! Woof!
           myCat.makeSound(); // Output: Cat meows: Meow!
           // Demonstrating polymorphic behavior with an array
           Animal[] animals = new Animal[3];
           animals[0] = new Animal();
           animals[1] = new Dog();
           animals[2] = new Cat();
           // Loop through all animals and call their methods
           for (Animal animal : animals) {
              animal.makeSound();
              animal.eat();
              System.out.println("-------------------");
           }
       }
   }
4. Activities
   🔷 Method Overloading – Activity Questions
   Activity 1: Calculator - Add Method
   Create a Calculator class that demonstrates method overloading by implementing
   multiple add() methods with different parameter types and counts.
   Activity 2: Display Utility
   Write a Display class with overloaded show() methods to print a message, a
   number, and a message multiple times. Demonstrate how method overloading
   provides flexibility with different parameter lists.
   Activity 3: Volume Calculator
   Implement a Volume class with overloaded calculate() methods to find the volume
   of a cube and a cuboid using method overloading. Test each variation with
   appropriate values.
   Activity 4: Greeting Message
   Develop a Greeting class that includes overloaded greet() methods:
   1. Without parameters,
   2. With a single string parameter for name,
   3. With both name and age. Demonstrate overloading by invoking each method.
   Activity 5: Area Finder
   Create an AreaFinder class that overloads the area() method to compute:
   ● Area of a circle (one double),
   ●   Rectangle (two doubles),
   ●   Triangle (two integers for base and height). Call all versions and display
       results.
   🔶 Method Overriding – Activity Questions
   Activity 6: Animal Sound
   Create a superclass Animal with a sound() method. Override this method in the
   Dog class to display a specific sound. Use a superclass reference to demonstrate
   runtime polymorphism.
   Activity 7: Employee Salary Calculation
   Design an Employee class with a method calculateSalary(). Override this method
   in a Manager subclass to return a higher salary. Show how polymorphism enables
   calling the correct method at runtime.
   Activity 8: Shape Drawing
   Build a Shape superclass with a draw() method and override it in a Circle class.
   Demonstrate method overriding using a Shape reference and calling the
   overridden method on a Circle object.
   Activity 9: Vehicle Start
   Write a Vehicle class with a start() method. Create a Car class that overrides this
   method. Use a Vehicle reference to call the start() method and observe runtime
   behavior.
   Activity 10: Payment Processing
   Create a Payment class with a method pay(). Then, create a CreditCardPayment
   subclass that overrides pay() with specific logic. Demonstrate polymorphism
   through superclass references.
5. Lab Tasks
Task 1: Shape Hierarchy
Create a base class Shape with a method calculateArea(). Create subclasses Circle,
Rectangle, and Triangle that override the calculateArea() method with their specific area
calculation formulas. Test using a Shape reference to call the overridden methods.
Task 2: Banking System
Create a base class BankAccount with a method calculateInterest(). Create subclasses
SavingsAccount and FixedDepositAccount that override the method with different interest
calculation logic. Demonstrate using a BankAccount reference.
Task 3: File Handling
Create a base class FileHandler with a method processFile(). Create subclasses
TextFileHandler and BinaryFileHandler that override the method with different file
processing implementations. Test with a FileHandler reference.
Task 4: Notification System
Create a base class Notification with a method send(). Create subclasses EmailNotification,
SMSNotification, and PushNotification that override the method with their specific sending
mechanisms. Demonstrate polymorphism using a Notification reference.
Task 5: Transportation
Create a base class Transport with methods move() and fuelConsumption(). Create
subclasses Car, Bicycle, and Airplane that override these methods. Test using a Transport
reference.
Task 6: String Manipulation
Create a class StringFormatter with overloaded methods format() that accept different
parameter combinations:
   ●   A string only (returns uppercase)
   ●   A string and boolean (if true returns uppercase, else lowercase)
   ●   A string and an integer (repeats the string n times)
   ●   A string, integer, and char (repeats the string n times with the char as separator)
Task 7: Geometric Calculation
Create a class GeometryCalculator with overloaded methods calculateArea() that:
   ●   Accept a single integer (calculates area of a square)
   ●   Accept two integers (calculates area of a rectangle)
   ●   Accept a single double (calculates area of a circle)
   ●   Accept three doubles (calculates area of a triangle using Heron's formula)
Task 8: Data Converter
Create a class DataConverter with overloaded methods convert() that:
   ●   Accept an integer array (returns a double array)
   ●   Accept a double array (returns an integer array)
   ●   Accept a string array (returns an integer array by parsing)
   ●   Accept a string and a format pattern (parses string to date)
Task 9: Print Utility
Create a class PrintUtil with overloaded methods print() that:
   ●   Accept a string (prints as is)
   ●   Accept an array (prints all elements)
   ●   Accept a collection (prints all elements)
   ●   Accept an object and boolean (prints object details if boolean is true)
Task 10: Math Operations
Create a class MathOperations with overloaded methods max() that:
   ●   Accept two integers (returns the larger one)
●   Accept three integers (returns the largest)
●   Accept an integer array (returns the maximum value)
●   Accept a list of integers (returns the maximum value)