Inheritance and Interfaces
Sharing behavior with extends, defining contracts with implements, and reusing code across unrelated classes with mixins.
阅读需 3 分钟
Dart gives you three distinct ways for one class to build on another, and they solve different problems: extends shares an implementation, implements enforces a contract, and mixins reuse behavior across classes that aren't otherwise related.
extends: inheriting behavior
class Animal {
String name;
Animal(this.name);
void makeSound() {
print('$name makes a sound');
}
}
class Dog extends Animal {
Dog(super.name);
@override
void makeSound() {
print('$name barks');
}
}
void main() {
final animal = Animal('Generic Animal');
final dog = Dog('Rex');
animal.makeSound(); // Generic Animal makes a sound
dog.makeSound(); // Rex barks
}Dog gets every field and method Animal has, for free, and @override marks makeSound as intentionally replacing the parent's version — the analyzer checks this actually matches a method on the superclass, catching typos that would otherwise silently create a brand-new, unrelated method instead. super.name in the constructor forwards the parameter straight to Animal's constructor.
Calling back into the parent's version from a subclass uses super:
class Puppy extends Dog {
Puppy(super.name);
@override
void makeSound() {
super.makeSound(); // Rex barks
print('...but it sounds like a squeak');
}
}A class can extends only one other class — Dart doesn't support multiple inheritance through extends, which avoids the ambiguity of two parents defining conflicting behavior.
implements: enforcing a contract
implements says "this class promises to provide everything in that type's interface," without inheriting any actual code:
abstract class Shape {
double area();
}
class Circle implements Shape {
double radius;
Circle(this.radius);
@override
double area() => 3.14159 * radius * radius;
}
class Square implements Shape {
double side;
Square(this.side);
@override
double area() => side * side;
}Every class in Dart implicitly defines an interface matching its public members, so any class can be used with implements — but abstract class is the idiomatic choice when a type exists purely to define a contract, since it can't be instantiated directly (Shape() would be an error). Unlike extends, a class can implements several types at once, which is how Dart lets a single class satisfy multiple contracts.
Mixins: reuse without inheritance
A mixin adds behavior to a class without it being a parent in the traditional sense — useful when several unrelated classes need the same capability:
mixin Flyable {
void fly() => print('Flying!');
}
mixin Swimmable {
void swim() => print('Swimming!');
}
class Duck extends Animal with Flyable, Swimmable {
Duck(super.name);
}
void main() {
final duck = Duck('Donald');
duck.fly(); // Flying!
duck.swim(); // Swimming!
}Duck still extends Animal for its core identity, but picks up fly() and swim() with two mixins that have no inheritance relationship to Animal or each other. This is Dart's answer to the diamond problem multiple inheritance usually causes: mixins are applied in a linear order, and a class can combine as many as it needs without the ambiguity of two full parent classes.
Choosing between them
- extends when a subclass genuinely is a kind of the parent, and should share its implementation.
- implements when you only need to guarantee a shape (a set of methods), not share any code.
- mixin when a capability applies across otherwise-unrelated classes, and doesn't fit a single-parent hierarchy.