Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggyRazorpayPhonePe

Polymorphism - Compile-Time (Overloading) vs Runtime (Overriding)

Choose your preparation mode3 modes available
🎯
Polymorphism - Compile-Time (Overloading) vs Runtime (Overriding)
mediumOOPAmazonGoogleMicrosoft

Imagine a smartphone app that can respond differently to a tap depending on the context - sometimes opening a photo, other times dialing a number. This flexibility is powered by polymorphism, a core OOP concept.

💡 Beginners often confuse overloading and overriding because both involve methods with the same name but differ in when and how the method to execute is chosen. Overloading is decided at compile time based on method signatures, while overriding is decided at runtime based on the actual object's class.
📋
Interview Question

Explain the difference between compile-time polymorphism (method overloading) and runtime polymorphism (method overriding) in object-oriented programming. How do they work internally, and what are their typical use cases?

Method Overloading: same method name, different parameters, resolved at compile timeMethod Overriding: subclass provides specific implementation, resolved at runtime via dynamic dispatchBinding: static (compile-time) vs dynamic (runtime) and role of vtable in overriding
💡
Scenario & Trace
ScenarioA payment processing system where the 'processPayment' method can accept different types of payment details (credit card, UPI, wallet).
At compile time, the compiler selects the correct 'processPayment' method based on the parameter types (overloading). At runtime, if a subclass overrides 'processPayment' for a specific payment type, the overridden method is invoked via dynamic dispatch (overriding).
ScenarioA graphics application where a base class 'Shape' has a method 'draw'. Different shapes like Circle and Rectangle override 'draw' to render themselves appropriately.
The program holds references to 'Shape' but at runtime calls the overridden 'draw' method of the actual shape object, enabling polymorphic behavior (runtime overriding).
  • What happens if overloaded methods differ only by return type?
  • What if a subclass overloads a method instead of overriding it?
  • How does polymorphism behave with static methods or private methods?
⚠️
Common Mistakes
Confusing overloading with overriding as the same concept

Interviewer doubts candidate’s grasp of polymorphism basics

✅ Emphasize that overloading is compile-time and overriding is runtime polymorphism

Believing overriding can happen without inheritance

Interviewer questions candidate’s understanding of OOP hierarchy

✅ Clarify that overriding requires subclassing and method signature matching

Thinking overloading is resolved at runtime

Candidate fails to explain static vs dynamic binding correctly

✅ Explain that overloading is resolved by the compiler using method signatures

Assuming private or static methods can be overridden

Interviewer flags misunderstanding of method visibility and binding

✅ Explain that private and static methods are bound statically and cannot be overridden

🧠
Basic Definition - What It Is
💡 This level covers the fundamental distinction you must clearly state to show basic understanding.

Intuition

Overloading is choosing among methods with the same name but different parameters at compile time; overriding is choosing the method implementation at runtime based on the object's actual type.

Explanation

Polymorphism allows objects to be treated as instances of their parent class while behaving differently based on their actual subclass. Compile-time polymorphism, or method overloading, means having multiple methods with the same name but different parameter lists within the same class. The compiler decides which method to call based on the arguments provided. Runtime polymorphism, or method overriding, happens when a subclass provides its own version of a method declared in the parent class. The method to execute is determined at runtime depending on the actual object's type, enabling dynamic behavior.

Memory Hook

💡 Think of overloading as choosing a tool from a toolbox by size (parameters) before starting work (compile time), and overriding as choosing the right tool during the job based on the material (runtime object type).

Illustrative Code

class Calculator:
    def add(self, a, b):
        return a + b

    def add(self, a, b, c=0):  # Overloading by default args
        return a + b + c

class AdvancedCalculator(Calculator):
    def add(self, a, b, c=0):  # Overriding
        print("Using AdvancedCalculator's add")
        return super().add(a, b, c)

calc = AdvancedCalculator()
print(calc.add(1, 2))
print(calc.add(1, 2, 3))

Interview Questions

❓ How do you distinguish overloading from overriding?
  • Overloading: same method name, different parameters, resolved at compile time
  • Overriding: subclass method replaces parent method, resolved at runtime
⏱
Depth Level
Interview Time30 seconds
Depthbasic

Covers the core definitions and differences; sufficient for screening rounds.

Interview Target: Minimum floor - never go below this

Knowing only this helps pass initial screening but won't impress in detailed technical interviews.

🧠
Mechanism Depth - How It Works
💡 This level explains internal workings and is expected in product company interviews.

Intuition

Overloading is resolved by the compiler using static binding, while overriding uses dynamic binding via vtables or equivalent mechanisms at runtime.

Explanation

In method overloading, the compiler uses the method signature (name + parameter types) to select the appropriate method during compilation. This is static binding because the decision is fixed before execution. Overriding involves a subclass redefining a method from its superclass. At runtime, the program uses dynamic dispatch to invoke the correct method based on the actual object's type. This is typically implemented using a virtual method table (vtable) that maps method calls to the correct function pointers. Languages like Java and C++ use vtables to enable this dynamic behavior. Overriding enables polymorphic behavior where the same method call can result in different behaviors depending on the object's class.

Memory Hook

💡 Imagine overloading as choosing a recipe before cooking (compile time), and overriding as adapting the recipe while cooking based on available ingredients (runtime).

Illustrative Code

class Shape:
    def draw(self):
        print("Drawing a generic shape")

class Circle(Shape):
    def draw(self):  # Overriding
        print("Drawing a circle")

class Rectangle(Shape):
    def draw(self):  # Overriding
        print("Drawing a rectangle")

shapes = [Circle(), Rectangle(), Shape()]
for shape in shapes:
    shape.draw()  # Runtime polymorphism via overriding

Interview Questions

❓ What internal mechanisms enable overriding but not overloading?
  • Overriding uses dynamic dispatch and vtables for runtime method resolution
  • Overloading uses static binding resolved by the compiler
  • Overriding requires inheritance and method signature matching
⏱
Depth Level
Interview Time2-3 minutes
Depthintermediate

Demonstrates understanding of binding types, vtables, and runtime behavior; expected for FAANG on-sites.

Interview Target: Target level for FAANG on-sites

Mastering this level distinguishes you from most candidates.

📊
Explanation Depth Levels
💡 Choose depth based on interview stage and company expectations.
LevelInterview TimeSuitable ForRisk
Basic Definition30sScreening callToo shallow for on-site interviews
Mechanism Depth2-3 minutesOn-site interviews at FAANG and top product companiesRequires solid understanding; missing details can lose points
💼
Interview Strategy
💡 Use this guide to structure your explanation clearly and confidently before every interview.

How to Present

Start with a clear definition of polymorphism and the two types: overloading and overriding.Give a relatable example or analogy to make the concept tangible.Explain the internal mechanism: static binding for overloading, dynamic binding for overriding.Discuss common edge cases and clarify misconceptions.

Time Allocation

Definition: 30s → Example: 1min → Mechanism: 2min → Edge cases: 30s. Total ~4min

What the Interviewer Tests

Interviewer checks if you can clearly differentiate overloading vs overriding, explain when each applies, and describe how the language/runtime handles method calls.

Common Follow-ups

  • Can you explain what happens if a subclass overloads a method instead of overriding it?
  • How do static methods relate to polymorphism?
💡 These follow-ups test deeper understanding and ability to handle tricky scenarios.
🔍
Pattern Recognition

When to Use

Asked when interviewer wants to test understanding of polymorphism, method dispatch, or OOP fundamentals.

Signature Phrases

'Explain compile-time vs runtime polymorphism''Compare method overloading and overriding''What happens when a subclass overrides a method?'

NOT This Pattern When

Similar Problems

Practice

(1/5)
1. When an object composed of multiple behaviors receives a request to perform an action, what is the typical sequence of internal steps that occur to fulfill this request?
easy
A. The object delegates the action to one or more composed behavior objects which execute their respective parts
B. The object checks flags internally and runs conditional code for each behavior
C. The object directly executes the action code inherited from its superclass
D. The object creates a new subclass instance dynamically to handle the action

Solution

  1. Step 1: Understand delegation in composition

    In composition, the main object delegates responsibilities to composed behavior objects rather than handling all logic itself.
  2. Step 2: Analyze each option

    The object directly executes the action code inherited from its superclass describes inheritance, not composition. The object checks flags internally and runs conditional code for each behavior implies flag-based conditional logic, which is less flexible. The object creates a new subclass instance dynamically to handle the action is not a typical or practical approach.
  3. Step 3: Confirm correct flow

    The composed behaviors receive the delegated call and execute their specific logic, enabling modular and maintainable design.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    Delegation to composed objects is the hallmark of composition-based design.
Hint: Composition means delegation to behavior objects, not direct inheritance execution.
Common Mistakes:
  • Confusing inheritance method calls with composition delegation
  • Assuming flags control behavior execution internally
2. Given the following thread-safe observer pattern code snippet, what will be the output after the sequence of operations shown below?
import threading

class Observer:
    def __init__(self, name):
        self.name = name
    def update(self, event_type, message):
        print(f"{self.name} received {event_type}: {message}")

class Subject:
    def __init__(self):
        self.lock = threading.Lock()
        self.observers = {}

    def subscribe(self, observer, event_type):
        with self.lock:
            if event_type not in self.observers:
                self.observers[event_type] = set()
            self.observers[event_type].add(observer)

    def unsubscribe(self, observer, event_type):
        with self.lock:
            if event_type in self.observers and observer in self.observers[event_type]:
                self.observers[event_type].remove(observer)
                if not self.observers[event_type]:
                    del self.observers[event_type]

    def notify(self, event_type, message):
        with self.lock:
            observers_snapshot = list(self.observers.get(event_type, []))
        for observer in observers_snapshot:
            observer.update(event_type, message)

subject = Subject()
obs1 = Observer('Obs1')
obs2 = Observer('Obs2')
subject.subscribe(obs1, 'eventA')
subject.subscribe(obs2, 'eventB')
subject.notify('eventA', 'Hello A')
subject.notify('eventB', 'Hello B')
subject.unsubscribe(obs1, 'eventA')
subject.notify('eventA', 'Hello again A')
What is printed?
easy
A. Obs1 received eventA: Hello A Obs2 received eventB: Hello B Obs1 received eventA: Hello again A
B. Obs1 received eventA: Hello A Obs1 received eventA: Hello again A Obs2 received eventB: Hello B
C. Obs2 received eventB: Hello B Obs1 received eventA: Hello again A
D. Obs1 received eventA: Hello A Obs2 received eventB: Hello B

Solution

  1. Step 1: Trace subscriptions and notifications

    Obs1 subscribes to 'eventA', Obs2 to 'eventB'. First notify('eventA') calls Obs1.update, printing "Obs1 received eventA: Hello A". Then notify('eventB') calls Obs2.update, printing "Obs2 received eventB: Hello B".
  2. Step 2: Trace unsubscribe and final notification

    Obs1 unsubscribes from 'eventA'. The final notify('eventA') finds no observers, so no output for Obs1. Therefore, the last notification does not print anything.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Unsubscribed observers do not receive notifications [OK]
Hint: Unsubscribed observers do not receive notifications [OK]
Common Mistakes:
  • Assuming unsubscribed observers still get notified
3. You have an object with nested mutable fields, and you need to create a new object instance such that changes to the nested fields in the new object do not affect the original. Which approach best guarantees this behavior?
easy
A. Manually copy only the top-level fields, sharing nested references to save memory
B. Use a greedy algorithm to selectively copy fields based on usage frequency
C. Serialize the object to JSON and deserialize it back to create a new instance
D. Recursively copy all nested objects to create independent duplicates

Solution

  1. Step 1: Understand the problem requirement

    The goal is to create a new object where nested mutable fields are independent, so changes in the copy do not affect the original.
  2. Step 2: Evaluate approaches

    Manual shallow copy (A) shares nested references, causing side effects. Greedy selective copying (B) is not a standard approach and risks missing nested objects. JSON serialization (D) can lose methods and fail on special objects. Recursive deep copy (C) ensures all nested objects are duplicated, preserving independence.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Deep copy duplicates nested objects -> no shared references [OK]
Hint: Deep copy duplicates nested objects fully [OK]
Common Mistakes:
  • Assuming shallow copy suffices for nested mutable fields
4. Considering extensibility in the Snake and Ladder game design, what is a key trade-off when embedding snakes and ladders directly as attributes inside the Board class versus modeling them as separate entities?
medium
A. Modeling snakes and ladders as separate entities increases runtime complexity significantly
B. Embedding snakes and ladders inside Board simplifies design but reduces flexibility to add new types of board elements later
C. Embedding snakes and ladders inside Board improves encapsulation and makes the Board immutable
D. Modeling snakes and ladders separately forces duplication of position data, increasing memory usage unnecessarily

Solution

  1. Step 1: Embedding snakes/ladders inside Board

    This approach simplifies initial design but tightly couples Board to these elements.
  2. Step 2: Impact on extensibility

    Tightly coupled design makes it harder to add new board elements (e.g., portals, traps) without modifying Board.
  3. Step 3: Modeling as separate entities

    Allows easy extension by adding new element types without changing Board internals.
  4. Step 4: Complexity and memory considerations

    Separate entities add minimal overhead; runtime complexity impact is negligible.
  5. Final Answer:

    Option B -> Option B
  6. Quick Check:

    Trade-off is between simplicity and extensibility, not runtime complexity or memory bloat.
Hint: Tight coupling simplifies now but blocks future extensions [OK]
Common Mistakes:
  • Thinking separate entities cause big runtime overhead
  • Believing embedding improves encapsulation and immutability
  • Assuming separate entities cause memory bloat
5. What is the time complexity of notifying observers in the thread-safe observer pattern implementation where observers are stored in a dictionary mapping event types to sets of observers, and a snapshot list is created during notification?
medium
A. O(n) where n is total number of all observers across all event types
B. O(k) where k is the number of observers subscribed to the notified event type
C. O(k + n) where k is observers for event type and n is total observers
D. O(1) constant time due to hash set usage

Solution

  1. Step 1: Identify the data structure and notification process

    Observers are stored per event type in sets. Notification locks and copies only the observers for the specific event type.
  2. Step 2: Analyze complexity of notify()

    Notify creates a snapshot list of observers for the event type (size k) and iterates over it, so time is proportional to k, not total n.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Notify cost depends only on observers for that event type [OK]
Hint: Notify complexity depends on observers for event type only [OK]
Common Mistakes:
  • Assuming notify iterates over all observers regardless of event type
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard