DEV Community

Cover image for Abstracts vs Interfaces in Dart
Hossein HassanNejad
Hossein HassanNejad

Posted on

Abstracts vs Interfaces in Dart

Introduction

The concepts of abstract Classes and interfaces are fundamental to object-oriented programming. They are used to define contracts and behaviors that classes must follow, but they serve different purposes and have distinct characteristics.

abstract Class

An abstract class is a special type of class that cannot be instantiated directly. Instead, it acts as a blueprint or template for other classes. Its primary purpose is to provide a common base that can be shared by multiple subclasses, while also enforcing specific methods or behaviors that those subclasses must implement.

In Dart, abstract classes support single inheritance, meaning a class can extend only one abstract class. They are versatile because they can include fields, constructors, and concrete methods (methods with implementation). Unlike interfaces, abstract classes can provide a partial implementation, allowing subclasses to inherit and reuse some behavior while requiring them to implement specific parts. This makes abstract classes a powerful tool for creating structured, reusable, and maintainable code.

Example

// Abstract class acting as a blueprint for vehicles
abstract class Vehicle {
  String name; // Field
  double maxSpeed; // Field

  // Constructor
  Vehicle(this.name, this.maxSpeed);

  // Abstract method (must be implemented by subclasses)
  void start();

  // Concrete method (default implementation)
  void stop() {
    print('$name has stopped.');
  }

  // Another concrete method
  void displayInfo() {
    print('Vehicle: $name, Max Speed: $maxSpeed km/h');
  }
}

// Subclass Car extending the abstract class Vehicle
class Car extends Vehicle {
  Car(String name, double maxSpeed) : super(name, maxSpeed);

  @override
  void start() {
    print('$name is starting... Vroom Vroom!');
  }
}

// Subclass Bicycle extending the abstract class Vehicle
class Bicycle extends Vehicle {
  Bicycle(String name, double maxSpeed) : super(name, maxSpeed);

  @override
  void start() {
    print('$name is starting... Pedal Pedal!');
  }
}

void main() {
  // Create instances of Car and Bicycle
  final car = Car('Tesla Model S', 250);
  final bicycle = Bicycle('Mountain Bike', 30);

  // Use methods from the abstract class and subclasses
  car.displayInfo(); // Output: Vehicle: Tesla Model S, Max Speed: 250 km/h
  car.start();       // Output: Tesla Model S is starting... Vroom Vroom!
  car.stop();        // Output: Tesla Model S has stopped.

  bicycle.displayInfo(); // Output: Vehicle: Mountain Bike, Max Speed: 30 km/h
  bicycle.start();       // Output: Mountain Bike is starting... Pedal Pedal!
  bicycle.stop();        // Output: Mountain Bike has stopped.
}
Enter fullscreen mode Exit fullscreen mode

interface

An interface is a contract or blueprint that defines a set of methods and properties a class must implement. It specifies what a class should do without dictating how it should do it. Interfaces enforce a consistent structure across unrelated classes, enabling polymorphism and allowing different classes to be used interchangeably as long as they implement the same interface.

Key characteristics of interfaces include:

  • They cannot be instantiated directly.
  • A class can implement multiple interfaces, allowing it to adhere to multiple contracts simultaneously.
  • Pure interfaces (in most languages) cannot have fields or constructors; they focus solely on defining behavior.
  • Interfaces promote loose coupling and modularity by separating what a class should do from how it does it.
  • They define a strict contract without implementation, ensuring flexibility and consistency in design.

By using interfaces, developers can create systems where different classes work together seamlessly, as long as they adhere to the same contract, making code more modular, reusable, and maintainable.

Example (abstract interface class)

// Interface defining a contract for objects that can be drawn
abstract interface class Drawable {
  void draw(); // Contract method (no implementation)
}

// Interface defining a contract for objects that can be resized
abstract interface class Resizable {
  void resize(double scale); // Contract method (no implementation)
}

// Class Circle implementing both Drawable and Resizable interfaces
class Circle implements Drawable, Resizable {
  @override
  void draw() {
    print('Drawing a Circle');
  }

  @override
  void resize(double scale) {
    print('Resizing Circle by scale $scale');
  }
}

// Class Square implementing the Drawable interface
class Square implements Drawable {
  @override
  void draw() {
    print('Drawing a Square');
  }
}

void main() {
  // Create instances of Circle and Square
  final circle = Circle();
  final square = Square();

  // Use methods from the interfaces
  circle.draw(); // Output: Drawing a Circle
  circle.resize(2.0); // Output: Resizing Circle by scale 2.0

  square.draw(); // Output: Drawing a Square

  // Polymorphism in action
  List<Drawable> shapes = [circle, square];
  for (var shape in shapes) {
    shape.draw(); // Calls the draw() method of each shape
  }
}
Enter fullscreen mode Exit fullscreen mode

interface class

An interface class can include both concrete methods (with implementation) and abstract methods (without implementation). It can also have fields and constructors. A class can implement multiple interface classes, and the interface class itself can be instantiated directly unless marked as abstract.

The primary use case for an interface class is when you want to define a contract while also providing default behavior that implementing classes can use or override. In other words, it defines a contract with optional default behavior, making it ideal for scenarios where you want to share common functionality across multiple classes while still allowing flexibility for customization.

If the interface class has no abstract methods (only concrete methods), it can be instantiated directly. However, if the interface class contains abstract methods, it cannot be instantiated directly unless those methods are implemented.

Example

Can Be Instantiated
interface class Drawable {
  void draw() {
    print('Drawing something');
  }
}

void main() {
  final drawable = Drawable(); // Can be instantiated
  drawable.draw(); // Output: Drawing something
}
Enter fullscreen mode Exit fullscreen mode
Cannot Be Instantiated
interface class Drawable {
  void draw(); // Abstract method
}

void main() {
  // final drawable = Drawable(); // Error: Cannot instantiate an abstract class
}
Enter fullscreen mode Exit fullscreen mode

Differences

Abstract classes are used when you want to provide a base implementation that subclasses can reuse, enforce specific methods that subclasses must implement, and share common behavior among related classes while offering a partial implementation. On the other hand, interfaces are used when you want to define a strict contract that multiple unrelated classes must follow, and enforce a consistent structure across different classes without providing any implementation. Abstract classes are ideal for creating a shared foundation, while interfaces are perfect for ensuring flexibility and consistency across diverse classes.

Feature abstract class abstract interface class interface class
Purpose Provides a partial implementation. Defines a pure interface (no implementation). Defines a contract with optional default behavior.
Methods Can have abstract and concrete methods. Can only have abstract methods. Can have concrete and abstract methods.
Fields Can have fields and constructors. Cannot have fields or constructors. Can have fields and constructors.
Instantiation Cannot be instantiated directly. Cannot be instantiated directly. Can be instantiated directly (unless marked as abstract).
Inheritance A class can extend only one abstract class. A class can implement multiple abstract interface classes. A class can implement multiple interface classes.
Use Case When you want to share common behavior among subclasses. When you want to enforce a strict contract. When you want to define a contract with optional default behavior.

Sentry blog image

The countdown to March 31 is on.

Make the switch from app center suck less with Sentry.

Read more

Top comments (0)

Qodo Takeover

Introducing Qodo Gen 1.0: Transform Your Workflow with Agentic AI

Rather than just generating snippets, our agents understand your entire project context, can make decisions, use tools, and carry out tasks autonomously.

Read full post