Inheritance, abstract classes and interfaces

Dart · Practical · 12 min

Flutter's widget hierarchy and most APIs depend on extends and implements, and Dart treats them differently from Java.

class Dog extends Animal inherits Animal's fields and methods. Override members with the @override annotation and call the parent with super.method(). Constructors aren't inherited; a subclass constructor forwards with super(...), or more concisely with super parameters: Dog(super.name);.

An abstract class can't be instantiated and can declare methods without bodies, which concrete subclasses must implement:

abstract class Shape {
  double get area;           // abstract getter
  String describe() => 'Shape with area $area';
}

Dart has no separate interface keyword in the Java sense: every class implicitly defines an interface consisting of its public members. class MockClient implements HttpClient must provide all of them itself — nothing is inherited. A class can extends only one class but implements many.

Use extends to reuse implementation and implements to promise a contract (common for test doubles). Since Dart 3, class modifiers such as interface, base and final let library authors restrict which of these is allowed outside their library.

Object is the root of all non-nullable types. toString, ==, hashCode and noSuchMethod come from it.

Type tests with is promote: if (shape is Circle) print(shape.radius);. Covariance of overridden parameters requires the covariant keyword.

Example

abstract class Shape {
  const Shape();
  double get area;
  String describe() => '$runtimeType with area ${area.toStringAsFixed(1)}';
}
class Rect extends Shape {
  final double w, h;
  const Rect(this.w, this.h);
  @override
  double get area => w * h;
}
class Square extends Rect {
  const Square(double side) : super(side, side);
  @override
  String describe() => 'Square! ${super.describe()}';
}
class FakeShape implements Shape {      // contract only, nothing inherited
  @override double get area => 1;
  @override String describe() => 'fake';
}
void main() {
  final shapes = <Shape>[Rect(2, 3), Square(2), FakeShape()];
  for (final s in shapes) print(s.describe());
  // Rect with area 6.0 / Square! Square with area 4.0 / fake
}

Remember

  • extends inherits code from one superclass
  • Every class is an implicit interface; implements copies only the contract
  • Abstract classes declare members subclasses must provide

Watch out for

  • Using implements and expecting to inherit method bodies.

Check yourself

  1. `class B implements A` where A has a concrete method `hello()`. What must B do?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: dart.dev/language/extend