Compile-time vs Runtime Polymorphism

Compile-time vs Runtime Polymorphism

About

  • Compile-time: Resolved during compilation (method overloading)
  • Runtime: Determined at execution (method overriding)
  • Dart’s Model: Primarily runtime polymorphism
  • Performance: Different tradeoffs

Main Topics

  1. Method Overloading

    • Definition: Same name, different parameters
    • Example:
      // Not directly supported in Dart
      // Use named parameters instead:
      void log(String message, {int priority}) {
        // Implementation
      }
  2. Method Overriding

    • Definition: Subclass redefines method

    • Example:

      class Animal {
        void speak() => print('Sound');
      }
      
      class Dog extends Animal {
        @override
        void speak() => print('Bark');
      }
  3. Static Dispatch

    • Definition: Compile-time resolution
    • Example:
      // Dart uses runtime dispatch for instance methods
      // Final/private methods may be statically resolved
  4. Dynamic Dispatch

    • Definition: Runtime method lookup
    • Example:
      Animal myAnimal = Dog();
      myAnimal.speak(); // Prints "Bark" (runtime decision)
  5. Performance Impact

    • Definition: Runtime costs
    • Example:
      // Virtual calls have small overhead
      // JIT may optimize frequent paths

How to Use

  • Flexibility: Leverage runtime polymorphism
  • Performance: Consider final methods
  • Design: Prefer composition where appropriate
  • Clarity: Use @override consistently

How It Works

  1. VTable: Virtual method table
  2. Lookup: Runtime type checking
  3. Optimization: JIT specialization
  4. Caching: Frequent call paths

Example:

abstract class Shape {
  void draw(); // Abstract method
}

class Circle implements Shape {
  @override
  void draw() => print('Drawing circle');
}

void render(Shape s) {
  s.draw(); // Dynamic dispatch
}

void main() {
  render(Circle()); // Prints "Drawing circle"
}

Conclusion

Dart primarily uses runtime polymorphism through method overriding and interface implementation, enabling flexible code structures while maintaining clear type hierarchies. The lack of traditional method overloading is compensated by named parameters and optional arguments.