SkillByAIOpen interactive version →

Lesson 11 / 25

Inheritance, Interfaces, Abstract Classes and Mixins

Reuse and share behaviour with extends, implements and mixins.

Three ways to share behaviour

Dart supports single inheritance with extends: a subclass inherits the superclass's members, overrides methods with @override, and calls the parent with super. Every class also defines an implicit interface: another class can implement it (or several), promising to provide all its members, without inheriting any code, which is how you create test fakes. Abstract classes (abstract class Shape) cannot be instantiated and can declare abstract methods for subclasses to implement. Mixins add reusable behaviour to multiple class hierarchies: declare mixin Timestamped { DateTime? updatedAt; void touch() => updatedAt = DateTime.now(); } and apply it with class Order extends Entity with Timestamped. A mixin can restrict which classes use it with on BaseClass. Mixins are applied in order, and later mixins override earlier ones. Prefer composition and small interfaces; use mixins for genuinely cross-cutting helpers, and inheritance for real is-a relationships.

extends, implements and with

An abstract base, an interface used for a fake, and a mixin.

abstract class PaymentGateway {
  Future<String> charge(String orderId, int amountPaise);
}

mixin Logging {
  void log(String message) => print('[${runtimeType}] $message');
}

class UpiGateway with Logging implements PaymentGateway {
  @override
  Future<String> charge(String orderId, int amountPaise) async {
    log('charging $amountPaise for $orderId');
    return 'upi_$orderId';
  }
}

class FakeGateway implements PaymentGateway {        // implements: no code inherited
  final charges = <(String, int)>[];
  @override
  Future<String> charge(String orderId, int amountPaise) async {
    charges.add((orderId, amountPaise));
    return 'fake_$orderId';
  }
}

abstract class Shape {
  double get area;
  String describe() => '$runtimeType with area ${area.toStringAsFixed(1)}';
}

class Rect extends Shape {
  Rect(this.w, this.h);
  final double w, h;
  @override
  double get area => w * h;
}

Family, job role and skills

extends is family: you inherit traits from one parent. implements is a job role: you promise to do the duties, however you learnt them. with adds skills, like cooking or first aid, that people from any family can pick up.

Quick check: In Dart, how can a class promise to provide another class's members without inheriting its code?

  • extends
  • implements
  • with
  • super
Answer

implements — Every class defines an implicit interface that other classes can implement.