Dart面向对象与Mixin机制—线性化规则与sealed class|新宇宙博客
class Vector2D {
final double x;
final double y;
// 主构造函数(Generative Constructor)
Vector2D(this.x, this.y); // 参数自动赋值给同名字段
// 命名构造函数(Named Constructor)
Vector2D.zero() : x = 0, y = 0;
Vector2D.fromAngle(double angle, double magnitude)
: x = magnitude * cos(angle),
y = magnitude * sin(angle);
// 工厂构造函数(Factory Constructor)——可返回子类或缓存实例
static final Map<String, Vector2D> _cache = {};
factory Vector2D.cached(double x, double y) {
final key = '$x,$y';
return _cache.putIfAbsent(key, () => Vector2D(x, y));
}
// 重定向构造函数
Vector2D.unit() : this(1, 0);
// const 构造函数(编译时常量)
const Vector2D.origin() : x = 0, y = 0;
// 初始化列表(Initializer List)——在 super() 之前执行
Vector2D.validated(this.x, this.y) : assert(x.isFinite && y.isFinite, '坐标必须是有限数');
// 运算符重载
Vector2D operator +(Vector2D other) => Vector2D(x + other.x, y + other.y);
Vector2D operator *(double scalar) => Vector2D(x * scalar, y * scalar);
@override
String toString() => 'Vector2D($x, $y)';
@override
bool operator ==(Object other) =>
other is Vector2D && x == other.x && y == other.y;
@override
int get hashCode => Object.hash(x, y);
}
1.2 getter / setter 与封装 class Temperature {
double _celsius;
Temperature(this._celsius);
// getter
double get celsius => _celsius;
double get fahrenheit => _celsius * 9 / 5 + 32;
double get kelvin => _celsius + 273.15;
// setter with validation
set celsius(double value) {
if (value < -273.15) throw RangeError('低于绝对零度!');
_celsius = value;
}
set fahrenheit(double value) => celsius = (value - 32) * 5 / 9;
}
1.3 级联操作符(Cascade Notation) // 避免重复引用同一对象
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
// 空感知级联
target?..method1()..method2(); // 若 target 为 null,跳过整个链
2. 接口:隐式接口的设计哲学 Dart 的一大独特之处:每个类都隐式地定义一个接口 ,无需 interface 关键字(Dart 3 新增了 interface 修饰符)。
// 任何类都可以被 implement(作为接口使用)
class Logger {
void log(String message) {
print('[LOG] $message');
}
}
// ✅ 实现 Logger 作为接口(不继承实现,只实现签名)
class SilentLogger implements Logger {
@override
void log(String message) {
// 静默,不做任何事
}
}
// ✅ 继承 Logger(继承实现)
class TimestampLogger extends Logger {
@override
void log(String message) {
print('[${DateTime.now()}] $message');
}
}
// Dart 3 新增的类修饰符(更精确的语义控制)
// interface class:只能被 implement,不能被 extends
interface class Shape {
double area();
double perimeter();
}
// abstract interface class:抽象接口(不能实例化)
abstract interface class Serializable {
Map<String, dynamic> toJson();
// 无需 factory,实现类自己定义 fromJson
}
// final class:不能被继承或 implement
final class Token {
final String value;
final DateTime expiry;
Token(this.value, this.expiry);
}
// base class:只能被继承(不能被 implement)
base class Animal {
void breathe() => print('呼吸');
}
// sealed class:密封类,子类必须在同一文件(用于穷举 switch)
sealed class NetworkResult<T> {}
class Success<T> extends NetworkResult<T> { final T data; Success(this.data); }
class Failure<T> extends NetworkResult<T> { final String message; Failure(this.message); }
class Loading<T> extends NetworkResult<T> {}
3. 抽象类与接口的边界 // abstract class:可以有实现,不能直接实例化
abstract class Repository<T, ID> {
// 抽象方法(子类必须实现)
Future<T?> findById(ID id);
Future<List<T>> findAll();
Future<T> save(T entity);
Future<void> delete(ID id);
// 具体方法(提供默认实现)
Future<bool> exists(ID id) async {
return await findById(id) != null;
}
// 使用模板方法模式
Future<T> findOrThrow(ID id) async {
final entity = await findById(id);
if (entity == null) throw NotFoundException('$T with id $id not found');
return entity;
}
}
// 具体实现
class UserRepository extends Repository<User, int> {
final Database _db;
UserRepository(this._db);
@override
Future<User?> findById(int id) => _db.query('SELECT * FROM users WHERE id = $id')
.then((rows) => rows.isEmpty ? null : User.fromRow(rows.first));
@override
Future<List<User>> findAll() => _db.query('SELECT * FROM users')
.then((rows) => rows.map(User.fromRow).toList());
@override
Future<User> save(User entity) async { /* ... */ return entity; }
@override
Future<void> delete(int id) => _db.execute('DELETE FROM users WHERE id = $id');
}
4. Mixin 的本质与线性化算法
4.1 Mixin 是什么 Mixin 是一种将行为注入类 的机制,不通过继承,而是通过线性化合并 方法解析顺序(MRO,Method Resolution Order)。
mixin Flyable {
String fly() => '${runtimeType} 在飞翔';
void describe() {
print('我能飞!');
}
}
mixin Swimmable {
String swim() => '${runtimeType} 在游泳';
void describe() {
print('我能游泳!');
}
}
// 使用 with 关键字混入
class Duck extends Animal with Flyable, Swimmable {
@override
void describe() {
// super.describe() 调用的是哪个?
super.describe(); // 调用 Swimmable.describe()(最后混入的优先)
print('我是鸭子!');
}
}
4.2 线性化算法详解 Dart 使用 C3 线性化算法 (与 Python 相同)确定方法调用顺序。
class A {
void hello() => print('A.hello');
}
mixin M1 {
void hello() => print('M1.hello');
void onlyM1() => print('只有 M1 有');
}
mixin M2 {
void hello() => print('M2.hello');
void onlyM2() => print('只有 M2 有');
}
class B extends A with M1, M2 {
// 方法解析顺序(MRO):B → M2 → M1 → A
// with M1, M2:后面的 Mixin 优先级更高
}
void main() {
final b = B();
b.hello(); // 输出:M2.hello(M2 最后混入,优先级最高)
// super 调用链
// B.hello() → M2.hello() → M1.hello() → A.hello()
}
// 理解 super 链的关键示例
mixin LogMixin {
void save() {
print('LogMixin: before save');
super.save(); // 调用 MRO 中下一个的 save
print('LogMixin: after save');
}
}
mixin ValidationMixin {
void save() {
print('ValidationMixin: 验证数据');
super.save(); // 调用 MRO 中下一个的 save
}
}
class BaseService {
void save() {
print('BaseService: 实际保存');
}
}
class UserService extends BaseService with ValidationMixin, LogMixin {
// MRO: UserService → LogMixin → ValidationMixin → BaseService
}
void testChain() {
UserService().save();
// 输出:
// LogMixin: before save
// ValidationMixin: 验证数据
// BaseService: 实际保存
// LogMixin: after save
}
class C extends A with M1, M2
↓ 等价于创建匿名类链:
class _C$M2 extends _C$M1 { /* M2 的方法 */ }
class _C$M1 extends A { /* M1 的方法 */ }
class C extends _C$M2 { /* C 自己的方法 */ }
↓ 方法解析顺序(从高到低):
C → M2 → M1 → A → Object
5. Mixin 的 on 约束 on 关键字指定 Mixin 只能应用在特定超类上,这允许在 Mixin 内部安全调用超类方法:
abstract class Scrollable {
void scrollTo(double offset);
double get currentOffset;
}
// 这个 Mixin 只能用于 Scrollable 的子类
mixin InfiniteScrollMixin on Scrollable {
bool _isLoading = false;
final List<dynamic> _items = [];
// 可以安全调用 Scrollable 的方法
void handleScroll() {
// currentOffset 和 scrollTo 来自 Scrollable
if (currentOffset > _getThreshold() && !_isLoading) {
loadMore();
}
}
double _getThreshold() => 0.8; // 80% 时预加载
Future<void> loadMore() async {
_isLoading = true;
final newItems = await fetchNextPage(_items.length);
_items.addAll(newItems);
_isLoading = false;
}
Future<List<dynamic>> fetchNextPage(int offset);
}
// ✅ 合法:BaseList 继承了 Scrollable
class BaseList extends Scrollable {
@override
void scrollTo(double offset) { /* ... */ }
@override
double get currentOffset => 0;
}
class ProductList extends BaseList with InfiniteScrollMixin {
@override
Future<List<dynamic>> fetchNextPage(int offset) async {
return await api.getProducts(offset: offset, limit: 20);
}
}
// ❌ 编译错误:AnotherClass 没有继承 Scrollable
// class AnotherClass with InfiniteScrollMixin { }
6. Flutter 中的 Mixin 实战 Flutter 框架大量使用 Mixin,理解它们的实现原理至关重要:
// 1. TickerProviderStateMixin / SingleTickerProviderStateMixin
class AnimatedButtonState extends State<AnimatedButton>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
@override
void initState() {
super.initState();
// vsync: this —— this 实现了 TickerProvider 接口
_controller = AnimationController(
vsync: this, // Mixin 提供了 TickerProvider 的实现
duration: const Duration(milliseconds: 200),
);
_scaleAnimation = Tween(begin: 1.0, end: 0.95).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
// 2. AutomaticKeepAliveClientMixin(保持 PageView 子页面状态)
class MyTabPageState extends State<MyTabPage>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true; // 告诉框架保持此页面状态
@override
Widget build(BuildContext context) {
super.build(context); // ⚠️ 必须调用,Mixin 需要此方法
return const ExpensiveWidget();
}
}
// 3. 自定义 Mixin:可监听的状态
mixin ChangeNotifierMixin on State {
final List<VoidCallback> _listeners = [];
void addListener(VoidCallback listener) => _listeners.add(listener);
void removeListener(VoidCallback listener) => _listeners.remove(listener);
void notifyListeners() {
if (mounted) { // 来自 State 的 mounted 属性(on State 约束保证)
for (final listener in _listeners.toList()) {
listener();
}
}
}
@override
void dispose() {
_listeners.clear();
super.dispose();
}
}
7. 与 Java / Kotlin / Scala 的对比
Java:接口默认方法
interface Flyable {
default String fly () {
return getClass().getSimpleName() + " 在飞翔" ;
}
}
interface Swimmable {
default String fly () {
return "游泳" ;
}
}
class Duck implements Flyable , Swimmable {
@Override
public String fly () {
return Flyable.super .fly();
}
}
Dart 的优势 :Mixin 线性化自动解决冲突,无需手动指定。
Kotlin:委托(Delegation)
interface Printer {
fun print (msg: String )
}
class ConsolePrinter : Printer {
override fun print (msg: String ) = println(msg)
}
class Logger (printer: Printer) : Printer by printer {
}
Scala:Trait // Scala Trait 最接近 Dart Mixin
trait Flyable {
def fly(): String = s"${getClass.getSimpleName} 在飞翔"
}
trait Swimmable {
def swim(): String = s"${getClass.getSimpleName} 在游泳"
}
class Duck extends Animal with Flyable with Swimmable
// Scala 的 trait 也使用线性化算法,与 Dart 相似
8. 实战案例:构建可复用行为库 // 可复用的行为 Mixin 库
// 1. 可缓存的网络请求
mixin CacheableMixin<T> {
final Map<String, T> _cache = {};
final Map<String, DateTime> _cacheTime = {};
Duration get cacheDuration => const Duration(minutes: 5);
Future<T> fetchWithCache(
String key,
Future<T> Function() fetcher,
) async {
final cached = _cache[key];
final cacheTime = _cacheTime[key];
if (cached != null && cacheTime != null) {
final age = DateTime.now().difference(cacheTime);
if (age < cacheDuration) return cached;
}
final result = await fetcher();
_cache[key] = result;
_cacheTime[key] = DateTime.now();
return result;
}
void invalidateCache([String? key]) {
if (key != null) {
_cache.remove(key);
_cacheTime.remove(key);
} else {
_cache.clear();
_cacheTime.clear();
}
}
}
// 2. 可日志的操作
mixin LoggableMixin {
String get logTag => runtimeType.toString();
void logInfo(String msg) => print('[$logTag][INFO] $msg');
void logError(String msg, [Object? error]) =>
print('[$logTag][ERROR] $msg${error != null ? ': $error' : ''}');
Future<T> loggedOperation<T>(
String operationName,
Future<T> Function() operation,
) async {
logInfo('开始: $operationName');
final start = DateTime.now();
try {
final result = await operation();
final elapsed = DateTime.now().difference(start).inMilliseconds;
logInfo('完成: $operationName (${elapsed}ms)');
return result;
} catch (e) {
logError('失败: $operationName', e);
rethrow;
}
}
}
// 3. 可重试的操作
mixin RetryableMixin {
int get maxRetries => 3;
Duration get retryDelay => const Duration(seconds: 1);
Future<T> withRetry<T>(Future<T> Function() operation) async {
for (int attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (e) {
if (attempt == maxRetries) rethrow;
await Future.delayed(retryDelay * (attempt + 1)); // 指数退避
}
}
throw StateError('不可达');
}
}
// 组合多个 Mixin
class ProductService with CacheableMixin<List<Product>>, LoggableMixin, RetryableMixin {
final ApiClient _api;
ProductService(this._api);
Future<List<Product>> getProducts({String? category}) async {
final cacheKey = 'products:${category ?? 'all'}';
return loggedOperation('获取商品列表', () {
return fetchWithCache(cacheKey, () {
return withRetry(() => _api.getProducts(category: category));
});
});
}
}
// 使用
void main() async {
final service = ProductService(ApiClient());
final products = await service.getProducts(category: 'electronics');
// 自动:日志记录 + 缓存 + 失败重试
print('获取到 ${products.length} 个商品');
}
9. 深度追问 Q1:Mixin 和普通类的区别是什么?可以用普通类 with 吗?
Dart 3 之前,任何没有声明非 Object 超类的类都可以作为 Mixin(class M {} 可以 with M)。Dart 3 之后,推荐使用 mixin 关键字,更语义化,且 mixin 不能被直接实例化,也不能使用 extends(只能用 on)。普通类用 with 在 Dart 2 中是合法的,Dart 3 引入了 mixin class 来表示"既是 Mixin 也可实例化"。
Q2:mixin on 和普通 Mixin 的性能差异?
没有运行时性能差异。on 约束是编译时检查,不影响运行时性能。Mixin 在 Dart 编译时被"展开"为类继承链,最终和手写的类继承相同。
Q3:为什么 Flutter 使用 Mixin 而不是普通继承来提供 TickerProvider?
因为 Flutter Widget 可能需要同时继承 State<T> 并获取 AnimationController 的 vsync 支持。如果用继承实现 TickerProvider,就要求 State<T> 类额外继承 TickerProvider,破坏了关注点分离。用 Mixin,你可以在已有继承链(State<T>)的基础上,按需 混入 TickerProvider 行为,更灵活、更符合组合优于继承的原则。
Mixin 中的实例字段也遵循线性化规则,但实际上字段不会被"覆盖"——每个 Mixin 的字段独立存在于对象中。如果多个 Mixin 有同名字段,会产生编译错误(字段冲突)。因此 Mixin 字段最好用 _ 前缀(私有化),减少冲突风险,或者将字段改为 getter 方法(子类可覆盖)。
10. 总结表格 特性 Dart Mixin Java 接口默认方法 Kotlin 委托 Scala Trait 多重混入 ✅ with M1, M2 ✅ implements I1, I2 ✅ by ✅ with T1 with T2 冲突解决 自动线性化(后者优先) 手动解决 手动解决 自动线性化 超类约束 on SuperClass无 无 无 可实例化 mixin class接口不可实例化 可 abstract class访问超类成员 on 约束 + superdefault 不能访问委托对象 super[Trait]类型检查 is MyMixin ✅instanceof I ✅委托不影响类型 isInstanceOf[T]
Mixin 使用原则 1. 行为复用(Behavior Reuse):Mixin 适合封装横切关注点(日志、缓存、重试)
2. 接口 + 行为:用 abstract class 定义契约,用 Mixin 提供默认行为
3. on 约束:当 Mixin 依赖超类能力时,用 on 明确表达依赖
4. 避免 Mixin 有太多状态:状态多的 Mixin 难以测试和推理
5. 命名清晰:Mixin 名一般以 -able、-Mixin 结尾,表达能力