7. How do you achieve polymorphism in Java?

Basic

7. How do you achieve polymorphism in Java?

Overview

Polymorphism in Java is a core concept that allows objects to be treated as instances of their parent class rather than their actual class. This enables a single function to deal with different types of objects at runtime, facilitating flexibility and reusability in object-oriented programming.

Key Concepts

  1. Method Overloading: Defining multiple methods with the same name but different parameter lists in the same class.
  2. Method Overriding: Redefining a method in a subclass that already exists in the parent class.
  3. Dynamic Method Dispatch: A mechanism by which a call to an overridden method is resolved at runtime rather than compile-time.

Common Interview Questions

Basic Level

  1. What is polymorphism in Java?
  2. Explain method overloading and method overriding with an example.

Intermediate Level

  1. How does Java achieve runtime polymorphism?

Advanced Level

  1. Discuss the impact of polymorphism on system design and performance.

Detailed Answers

1. What is polymorphism in Java?

Answer: Polymorphism in Java is a concept that allows one interface to be used for a general class of actions. The specific action is determined by the exact nature of the situation. There are two types of polymorphism in Java: compile-time (static) and runtime (dynamic). Compile-time polymorphism is achieved through method overloading, while runtime polymorphism is achieved through method overriding.

Key Points:
- Polymorphism allows for flexibility and reusability in code.
- Enables the concept of one interface, multiple methods.
- It leverages Java's inheritance and interface features to achieve polymorphism.

Example:

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

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

public class TestPolymorphism {
    public static void main(String args[]) {
        Animal myAnimal = new Dog();  // Upcasting
        myAnimal.sound();  // Outputs: Dog barks
    }
}

2. Explain method overloading and method overriding with an example.

Answer: Method overloading occurs when two or more methods in the same class have the same name but different parameters. It is a compile-time polymorphism. Method overriding happens when a method in a subclass has the same name and type signature as a method in the parent class, enabling runtime polymorphism.

Key Points:
- Method overloading enhances program readability and reusability.
- Method overriding provides a specific implementation of a method that is already provided by its superclass.
- Static methods can be overloaded but cannot be overridden.

Example:

// Method Overloading
class Display {
    void show(int n) {
        System.out.println("Integer: " + n);
    }
    void show(String s) {
        System.out.println("String: " + s);
    }
}

// Method Overriding
class Parent {
    void show() {
        System.out.println("Parent's show()");
    }
}
class Child extends Parent {
    @Override
    void show() {
        System.out.println("Child's show()");
    }
}

public class Test {
    public static void main(String args[]) {
        Display obj = new Display();
        obj.show(10);        // Outputs: Integer: 10
        obj.show("Java");    // Outputs: String: Java

        Parent obj1 = new Child();
        obj1.show();         // Outputs: Child's show()
    }
}

3. How does Java achieve runtime polymorphism?

Answer: Java achieves runtime polymorphism through method overriding and dynamic method dispatch. In this mechanism, the overridden method call to the reference of a superclass is resolved at runtime, allowing an object to override functionality of the superclass.

Key Points:
- Dynamic method dispatch is crucial for runtime polymorphism.
- It happens in methods of objects that have been overridden.
- The determination of which overridden method to call is made at runtime, not at compile-time.

Example:

class Shape {
    void draw() {
        System.out.println("Drawing a shape");
    }
}
class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("Drawing a circle");
    }
}
public class TestPolymorphismRuntime {
    public static void main(String args[]) {
        Shape s = new Circle();  // Upcasting
        s.draw();  // Outputs: Drawing a circle, determined at runtime
    }
}

4. Discuss the impact of polymorphism on system design and performance.

Answer: Polymorphism significantly influences system design by enhancing the flexibility and extensibility of the code. It allows systems to be more modular, making it easier to understand, maintain, and extend. However, it can have a slight impact on performance due to the overhead of dynamic method dispatch, as it requires additional steps to determine the actual method to call at runtime.

Key Points:
- Improves code reusability and extensibility.
- Facilitates the use of interfaces and abstract classes for more flexible and maintainable code.
- The runtime determination of method calls introduces a negligible performance overhead.

Example:

// Demonstrating the design aspect
interface Payment {
    void processPayment(double amount);
}
class CreditCardPayment implements Payment {
    public void processPayment(double amount) {
        System.out.println("Processing credit card payment of $" + amount);
    }
}
class PaypalPayment implements Payment {
    public void processPayment(double amount) {
        System.out.println("Processing PayPal payment of $" + amount);
    }
}
public class PaymentProcessor {
    public static void process(Payment payment, double amount) {
        payment.processPayment(amount);
    }
    public static void main(String[] args) {
        Payment creditCardPayment = new CreditCardPayment();
        Payment paypalPayment = new PaypalPayment();

        process(creditCardPayment, 100.0);
        process(paypalPayment, 200.0);
    }
}

This example demonstrates how polymorphism can be used to design a flexible and extendable payment processing system, allowing for easy addition of new payment methods without altering the existing codebase significantly.