Quiz 2

Polymorphism — Overloading, Dynamic Dispatch, and instanceof

1056 words
5 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# Polymorphism — Overloading, Dynamic Dispatch, and instanceof ## 🎯 Learning Objectives By the end of this topic, you will be able to: - Distinguish compile-time (overloading) from runtime (overriding) polymorphism - Write overloaded methods correctly - Trace dynamic dispatch through class hierarchies - Safely use...

Polymorphism — Overloading, Dynamic Dispatch, and instanceof

🎯 Learning Objectives

By the end of this topic, you will be able to:
  • Distinguish compile-time (overloading) from runtime (overriding) polymorphism
  • Write overloaded methods correctly
  • Trace dynamic dispatch through class hierarchies
  • Safely use instanceof and casting
  • Explain how the JVM resolves method calls at runtime

📋 Prerequisites


1. Intuition

Compile-time polymorphism (overloading): Same name, different parameters — like a chef who can cook eggs in different ways (fried, scrambled, boiled). The recipe chosen depends on what ingredients you provide. Runtime polymorphism (overriding): Same name, same parameters, different behavior — like different chefs each making their signature pasta dish. You ask for "pasta" and get the chef's version. The actual dish depends on which chef you got.

2. Compile-Time Polymorphism (Method Overloading)

2.1 Rules

java
public class Printer {
    // Same name, different parameters
    public void print(int x) { }
    public void print(String s) { }
    public void print(int x, String s) { }
    public void print(String s, int x) { }  // Different order
}
Rules:
  • Methods must have the same name
  • Must have different parameter lists (type, count, or order)
  • Return type CAN differ but can't be the sole differentiator
  • Resolved at compile time

2.2 Why Overloading?

java
System.out.println(42);         // print(int)
System.out.println("Hello");    // print(String)
System.out.println(3.14);       // print(double)
Overloading provides convenience — one method name for logically similar operations.

2.3 Autoboxing and Widening in Overloading

When the compiler selects which overloaded method to call, it prefers (in order):
  1. Exact match (no conversion needed)
  2. Widening (e.g., int → long → double)
  3. Autoboxing (int → Integer)
  4. Varargs
java
public class OverloadDemo {
    static void go(int x) { System.out.println("int"); }
    static void go(long x) { System.out.println("long"); }
    static void go(Integer x) { System.out.println("Integer"); }
    static void go(int... x) { System.out.println("varargs"); }
    public static void main(String[] args) {
        go(5);  // "int" — exact match wins
    }
}

3. Runtime Polymorphism (Dynamic Dispatch)

3.1 How Dynamic Dispatch Works

java
class Animal {
    void sound() { System.out.println("Animal makes sound"); }
}
class Dog extends Animal {
    @Override
    void sound() { System.out.println("Dog barks"); }
}
class Cat extends Animal {
    @Override
    void sound() { System.out.println("Cat meows"); }
}
public class Test {
    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat(), new Animal()};
        for (Animal a : animals) {
            a.sound();  // JVM decides at runtime which sound() to call
        }
    }
}
// Output:
// Dog barks
// Cat meows
// Animal makes sound

3.2 Virtual Method Table (VTable)

The JVM maintains a vtable for each class — a table of method pointers. When a.sound() is called:
  1. JVM gets the actual class of a (Dog, Cat, or Animal)
  2. Looks up sound in that class's vtable
  3. Calls the found implementation (Diagram)

3.3 Polymorphism Enables Flexibility

java
public class PaymentProcessor {
    public void processPayment(PaymentMethod method, double amount) {
        method.pay(amount);  // Works with ANY PaymentMethod subclass
    }
}
// Later, add new payment types without changing processPayment!
class CreditCard extends PaymentMethod { void pay(double a) { /* ... */ } }
class UPI extends PaymentMethod { void pay(double a) { /* ... */ } }
class Crypto extends PaymentMethod { void pay(double a) { /* ... */ } }

4. Upcasting and Downcasting

java
Dog dog = new Dog();
Animal animal = dog;  // Upcast: implicit, safe
Dog dog2 = (Dog) animal;  // Downcast: explicit, risky

4.1 Upcasting

java
public void feed(Animal a) { a.eat(); }  // Accepts any Animal subclass
feed(new Dog());   // Dog upcast to Animal
feed(new Cat());   // Cat upcast to Animal

4.2 Downcasting with instanceof

java
Animal a = new Dog();
if (a instanceof Dog) {
    Dog d = (Dog) a;  // Safe
    d.bark();
}
if (a instanceof Cat) {
    Cat c = (Cat) a;  // Won't execute
}
Java 16+ Pattern Matching for instanceof:
java
if (a instanceof Dog d) {
    d.bark();  // No explicit cast needed!
}

5. Polymorphism and Method Binding Summary

TypeAlso calledWhen resolvedBased onMechanism
OverloadingStatic/compile-time polymorphismCompile timeReference type + parameter typesCompiler selects method
OverridingDynamic/runtime polymorphismRuntimeActual object typeVTable lookup

6. Common Pitfalls

Pitfall 1: Overloading Instead of Overriding (Missing @Override)

Pitfall 2: Fields Are Not Polymorphic (shadowing)

Pitfall 3: Static Methods Use Static Binding (no dynamic dispatch)

Pitfall 4: Downcasting Without instanceof → ClassCastException


7. Practice Questions

Q1: What is the output?
java
class A { String f() { return "A"; } }
class B extends A { String f() { return "B"; } }
public class Test {
    public static void main(String[] args) {
        A obj = new B();
        System.out.println(obj.f());
    }
}
Answer: B — dynamic dispatch calls B's f(). Q2: What is the difference between overloading and overriding?
Answer: Overloading: same name, different params, compile-time. Overriding: same signature, different class, runtime. Q3: Can you overload by changing return type only?
Answer: No. int foo() and double foo() are ambiguous — compiler can't tell which to call. Q4: What does instanceof return for null?
Answer: false. null instanceof AnyType always returns false (no NullPointerException). Q5: What is the output?
java
class Parent {
    static void greet() { System.out.println("Parent static"); }
    void hello() { System.out.println("Parent hello"); }
}
class Child extends Parent {
    static void greet() { System.out.println("Child static"); }
    void hello() { System.out.println("Child hello"); }
}
public class Test {
    public static void main(String[] args) {
        Parent p = new Child();
        p.greet();
        p.hello();
    }
}
Answer: "Parent static" then "Child hello". Static methods use static binding (reference type). Instance methods use dynamic binding (object type).

📐 Key Concepts

ConceptResolutionUse
OverloadingCompile-time (reference type)Same operation, different inputs
OverridingRuntime (object type)Polymorphic behavior
UpcastingImplicitTreat child as parent
DowncastingExplicit + instanceofAccess child-specific features
instanceofRuntime checkSafe downcasting
Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.