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 继承了 name 和 describe()。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
}
变量的静态类型决定编译期可见成员,实际对象决定重写方法的实现。类型检查成功后,流程分析通常会把变量提升为更具体的类型。
extends 与 implements
每个类声明都会隐式定义一个接口:
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 的每个类同时隐式定义接口,并通过base、interface、final、sealed等修饰符更细致地控制库外继承和实现方式。
常见误区
- 构造函数不是普通实例成员,不会被子类继承。
implements不会复用方法体;需要复用超类实现时使用extends。- 重写参数类型时随意收窄会破坏替换能力,通常应保持相同或更宽的接受范围。
- 深层继承会让状态和行为来源难以追踪;仅复用一小段行为时,可以考虑组合或 Mixin。
小结
extends让子类继承一个超类的实例实现。- 子类必须声明自己的构造函数,并完成超类初始化。
- 使用
@override明确重写,使用super复用超类实现。 - 超类变量可以引用子类对象,重写方法会按运行时对象动态分派。
implements只继承契约;类修饰符进一步限制库外的继承方式。