Interfaces in Dart

In Dart, interfaces define a contract that classes must follow. Think of an interface as a blueprint that specifies what methods and properties a class should have, without providing the actual implementation. It's like having a job description that lists all the responsibilities, but doesn't tell you exactly how to do each task.

Understanding Interfaces

Unlike some other programming languages, Dart doesn't have a separate interface keyword. Instead, every class in Dart implicitly defines an interface. This means you can use any class as an interface by implementing it in another class.

Implementing Interfaces

To implement an interface in Dart, you use the implements keyword. When a class implements an interface, it must provide concrete implementations for all the methods and properties defined in that interface.

// Define a class that will serve as an interface
class Animal {
  void makeSound() {
    print("Some generic animal sound");
  }
  
  void move() {
    print("Animal is moving");
  }
}

// Implement the Animal interface
class Dog implements Animal {
  @override
  void makeSound() {
    print("Woof! Woof!");
  }
  
  @override
  void move() {
    print("Dog is running");
  }
}

class Cat implements Animal {
  @override
  void makeSound() {
    print("Meow!");
  }
  
  @override
  void move() {
    print("Cat is sneaking");
  }
}

Multiple Interface Implementation

One of the powerful features of interfaces is that a class can implement multiple interfaces. This allows for flexible design patterns and code reusability.

class Flyable {
  void fly() {
    print("Flying high!");
  }
}

class Swimmable {
  void swim() {
    print("Swimming gracefully!");
  }
}

// A class can implement multiple interfaces
class Duck implements Animal, Flyable, Swimmable {
  @override
  void makeSound() {
    print("Quack! Quack!");
  }
  
  @override
  void move() {
    print("Duck is waddling");
  }
  
  @override
  void fly() {
    print("Duck is flying over the pond");
  }
  
  @override
  void swim() {
    print("Duck is swimming in the water");
  }
}

Abstract Classes as Interfaces

You can also create abstract classes to serve as more formal interfaces. Abstract classes can contain both abstract methods (without implementation) and concrete methods (with implementation).

abstract class Vehicle {
  // Abstract method - must be implemented
  void startEngine();
  
  // Concrete method - can be inherited or overridden
  void honk() {
    print("Beep beep!");
  }
}

class Car implements Vehicle {
  @override
  void startEngine() {
    print("Car engine started with a key");
  }
  
  @override
  void honk() {
    print("Car horn: HONK HONK!");
  }
}

class Motorcycle implements Vehicle {
  @override
  void startEngine() {
    print("Motorcycle engine started with a button");
  }
  
  // Using the inherited honk method
}

Key Benefits of Interfaces

BenefitDescription
FlexibilityClasses can implement multiple interfaces
ConsistencyEnsures all implementing classes have required methods
PolymorphismDifferent classes can be treated uniformly
TestingEasy to create mock implementations for testing

Interface vs Inheritance

It's important to understand the difference between implementing an interface and extending a class:

  • Implements: You must provide your own implementation for all methods
  • Extends: You inherit the parent's implementation and can optionally override methods
// Using implements - must provide all implementations
class Robot implements Animal {
  @override
  void makeSound() {
    print("Beep boop!");
  }
  
  @override
  void move() {
    print("Robot is walking mechanically");
  }
}

// Using extends - inherits parent's implementation
class Puppy extends Dog {
  @override
  void makeSound() {
    print("Yip yip!"); // Override parent's implementation
  }
  // move() method is inherited from Dog
}

Interfaces in Dart provide a powerful way to define contracts and create flexible, maintainable code. They're especially useful when you want to ensure that different classes follow the same structure while allowing for different implementations.