跳到主要内容

Extend a class(继承类)

使用 extends 可以让一个类继承另一个类的实例成员,并在此基础上添加或改写行为。Dart 类是单继承的:一个类最多直接继承一个超类,但可以同时实现多个接口或应用多个 Mixin。

声明子类

class Animal {
final String name;

Animal(this.name);

void describe() {
print('Animal: $name');
}
}

class Dog extends Animal {
Dog(super.name);

void bark() {
print('$name says woof');
}
}

void main() {
final dog = Dog('Dash');
dog.describe(); // Animal: Dash
dog.bark(); // Dash says woof
}

Dog 继承了 namedescribe()Dog(super.name) 使用超类参数,把接收到的值转发给 Animal 构造函数。

构造函数不会继承

子类不会继承超类的构造函数。子类构造函数必须完成自己的字段初始化,并调用合适的超类构造函数:

class Vehicle {
final String id;

Vehicle.registered(this.id);
}

class Car extends Vehicle {
final int seats;

Car(String id, this.seats) : super.registered(id);
}

如果没有显式写 super(...),Dart 会尝试调用超类无参数、无名称的构造函数。该构造函数不存在时,代码无法通过分析。

重写成员

子类可以重写实例方法、getter、setter 和运算符。使用 @override 能让分析器检查你是否真的覆盖了已有成员:

class Animal {
String speak() => '...';
}

class Cat extends Animal {

String speak() => 'meow';
}

重写方法的返回类型必须与原成员兼容,参数类型通常不能收窄,命名参数等签名结构也必须保持兼容。这样,通过超类类型使用子类对象时仍然安全。

使用 super

在子类实例成员中,super 用于访问超类实现:

class Logger {
void log(String message) {
print(message);
}
}

class TimestampLogger extends Logger {

void log(String message) {
super.log('[${DateTime.now().toIso8601String()}] $message');
}
}

super.log(...) 调用的是超类版本,可以在保留原行为的同时增加处理。super 不是一个可保存到变量或传递给其他函数的普通对象。

多态与类型检查

子类对象可以赋给超类类型变量,调用哪个重写实现由对象的运行时类型决定:

class Shape {
double area() => 0;
}

class Square extends Shape {
final double side;

Square(this.side);


double area() => side * side;
}

void main() {
Shape shape = Square(4);

print(shape.area()); // 16.0
print(shape is Square); // true
}

变量的静态类型决定编译期可见成员,实际对象决定重写方法的实现。类型检查成功后,流程分析通常会把变量提升为更具体的类型。

extendsimplements

每个类声明都会隐式定义一个接口:

  • extends 继承实现,只能有一个直接超类。
  • implements 只接受接口契约,不复用实现,可以实现多个接口。
class Printable {
String format() => 'default';
}

class Report implements Printable {

String format() => 'report';
}

Report 必须自行实现 Printable 接口中的全部实例成员,不能直接使用 format() 的原实现。

类修饰符的影响

Dart 3 的类修饰符可以限制库外代码如何使用类:

  • base:库外可以继承,但不能仅通过 implements 实现。
  • interface:库外可以实现,但不能继承实现。
  • final:库外既不能继承,也不能实现。
  • sealed:直接子类型必须位于同一个库中,适合封闭类型层级。

这些限制主要作用于声明所在库之外;设计公共 API 时,应根据是否允许外部继承或实现来选择修饰符。

TypeScript 对比:两种语言的 extends 都表示单类继承。Dart 的每个类同时隐式定义接口,并通过 baseinterfacefinalsealed 等修饰符更细致地控制库外继承和实现方式。

常见误区

  • 构造函数不是普通实例成员,不会被子类继承。
  • implements 不会复用方法体;需要复用超类实现时使用 extends
  • 重写参数类型时随意收窄会破坏替换能力,通常应保持相同或更宽的接受范围。
  • 深层继承会让状态和行为来源难以追踪;仅复用一小段行为时,可以考虑组合或 Mixin。

小结

  • extends 让子类继承一个超类的实例实现。
  • 子类必须声明自己的构造函数,并完成超类初始化。
  • 使用 @override 明确重写,使用 super 复用超类实现。
  • 超类变量可以引用子类对象,重写方法会按运行时对象动态分派。
  • implements 只继承契约;类修饰符进一步限制库外的继承方式。