Dart 面向对象:类、Mixin 线性化与 Extension
mediumdartoopmixinextensionsealed
Dart 的类体系
Dart 是一门纯面向对象语言:所有值都是对象,包括数字、函数、null。所有类都隐式继承自 Object。
构造函数的多种形式
Dart 的构造函数设计针对 Flutter 场景做了大量优化。
初始化列表
class Point {
final double x;
final double y;
// 简洁的命名参数构造 + 初始化语法糖
Point({required this.x, required this.y});
// 等价的完整写法:
// Point({required double x, required double y})
// : this.x = x, this.y = y;
}
this.x 语法糖告诉编译器:「把同名参数的值直接赋给字段」,省去了冗余的赋值代码。
命名构造函数
class Color {
final int r, g, b;
Color(this.r, this.g, this.b);
// 命名构造函数:可以有多个
Color.fromHex(String hex) : this(
int.parse(hex.substring(1, 3), radix: 16),
int.parse(hex.substring(3, 5), radix: 16),
int.parse(hex.substring(5, 7), radix: 16),
);
Color.white() : this(255, 255, 255);
Color.black() : this(0, 0, 0);
}
工厂构造函数
class Logger {
static final _instances = <String, Logger>{};
final String name;
Logger._internal(this.name);
// factory 构造函数可以不创建新对象
factory Logger(String name) {
return _instances.putIfAbsent(name, () => Logger._internal(name));
}
}
// 调用方式与普通构造一样,但实际上可能返回缓存对象
final logger1 = Logger('http');
final logger2 = Logger('http');
print(identical(logger1, logger2)); // true,同一个实例
继承与接口的统一
Dart 没有 interface 关键字。所有类都天然可以作为接口:
class Animal {
void breathe() => print('breathing...');
void eat() => print('eating...');
}
// extends:继承实现(单继承)
class Dog extends Animal {
@override
void eat() => print('Dog eating...');
}
// implements:实现接口(多实现,必须重写所有方法)
class Robot implements Animal {
@override
void breathe() => print('Robot sim breathing');
@override
void eat() => print('Robot sim eating');
}
implements 借用了类定义作为接口,但不继承任何实现,必须全部重写。
Mixin:代码复用的第三条路
继承是一条路(单继承树),接口是第二条(无实现)。Mixin 是第三条:把方法集合「混入」类,不建立继承关系,可以多个混入。
可以把 Mixin 想象成「配料包」:一碗泡面(主类)可以加辣椒包(Mixin A),可以加芝麻包(Mixin B),两个配料包不互相干涉,都融入了这碗面。
基本语法
mixin Flyable {
void fly() => print('$runtimeType is flying');
double get maxAltitude => 10000.0;
}
mixin Swimmable {
void swim() => print('$runtimeType is swimming');
}
class Duck extends Animal with Flyable, Swimmable {
// Duck 同时具有飞行和游泳能力
}
Duck().fly(); // Duck is flying
Duck().swim(); // Duck is swimming
Mixin 的线性化(C3 算法)
当多个 Mixin 包含同名方法时,Dart 使用 C3 线性化算法 确定方法解析顺序(MRO)。
理解方式:把 with A, B, C 想象成一条从右到左的查找链:先查最右边的,然后依次往左,最后是主类和父类。
mixin A {
String hello() => 'A';
}
mixin B {
String hello() => 'B';
}
class MyClass with A, B {
// B 在 A 的右边,B 更靠近 MyClass 本身
}
// 线性化顺序:MyClass → B → A → Object
MyClass().hello(); // 输出 'B'(B 最后混入,优先级最高)
线性化规则的精确表述:
class MyClass with A, B
→ 等价于:
class _MyClassWithA extends Object with A {}
class _MyClassWithAB extends _MyClassWithA with B {}
class MyClass extends _MyClassWithAB {}
→ MRO(方法解析顺序):MyClass → B → A → Object
on 约束 Mixin
可以用 on 限制 Mixin 只能用于特定类型:
mixin Animated on State<StatefulWidget> {
// 这个 Mixin 只能被 State 子类使用
// 可以安全调用 setState、mounted 等 State 方法
void startAnimation() {
if (mounted) setState(() { /* ... */ });
}
}
class MyState extends State<MyWidget> with Animated {
// ✅ 合法,MyState extends State
}
class Foo with Animated {
// ❌ 编译错误:Foo 不是 State 的子类
}
Extension:为现有类添加方法
Extension 允许在不修改原有类的情况下,为它增加方法。
基本语法
extension StringExtensions on String {
// 为 String 添加方法
bool get isEmail => contains('@') && contains('.');
String capitalize() {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
}
// 使用:和普通方法完全一样
print('hello'.capitalize()); // Hello
print('user@test.com'.isEmail); // true
泛型 Extension
extension ListExtensions<T> on List<T> {
T? get firstOrNull => isEmpty ? null : first;
List<T> interleave(T separator) {
final result = <T>[];
for (int i = 0; i < length; i++) {
if (i > 0) result.add(separator);
result.add(this[i]);
}
return result;
}
}
[1, 2, 3].interleave(0); // [1, 0, 2, 0, 3]
Extension 的局限性
Extension 只能添加方法,不能:
- 添加实例字段(只能用 getter 模拟)
- 重写已有方法(优先级低于类本身)
- 被子类继承
Sealed Class(Dart 3.0+)
sealed 关键字限制类的继承:只有同一个文件内的类可以继承它,外部文件不能。
// shapes.dart
sealed class Shape {}
class Circle extends Shape { final double radius; Circle(this.radius); }
class Square extends Shape { final double side; Square(this.side); }
// 其他文件不能继承 Shape
// 使用处:switch 有详尽性检查
double area(Shape s) => switch (s) {
Circle(:final radius) => 3.14 * radius * radius,
Square(:final side) => side * side,
// ✅ 编译器知道 Shape 只有这两个子类,无需 default
};
sealed + switch 是 Dart 3.0 对「代数数据类型」的原生支持,在 Flutter 的 BLoC/Riverpod 状态建模中被广泛应用。