背景
- Dart 不支持类的多继承(只能
extends
一个父类)。 - 但是,有时候你希望一个类获得多个不同类的功能。
- 解决方案:Mixin(混入),用
with
关键字实现类似“多继承”的功能。
基本使用
// 在 Dart 之前的版本,mixin 是可以不加在类名前面的
mixin class Phone {
void call() {
print('Phone is calling...');
}
}
mixin class Android {
void playStore() {
print('Google play store');
}
}
mixin class Ios {
void appleStore() {
print('Apply store');
}
}
class Xiaomi with Phone, Android, Ios {}
void main(List<String> args) {
var p = Xiaomi();
p.call(); // 调用 Phone 的方法
p.playStore(); // 调用 Android 的方法
p.appleStore(); // 调用 Ios 的方法
}
函数重名冲突
在 Dart 里,with
多个 mixin 时,如果方法名字冲突,会按照 with
的从左到右的顺序 来“覆盖”前面的实现。
换句话说,后面的 mixin 优先级更高。
mixin class Phone {
void call() {
print('Phone is calling...');
}
}
mixin class Android {
void playStore() {
print('Google play store');
}
void call() {
print('Android phone is calling...');
}
}
mixin class Ios {
void appleStore() {
print('Apply store');
}
void call() {
print('Ios phone is calling...');
}
}
class Xiaomi with Phone, Android, Ios {}
// 注意:如果有多个 mixin 中有同名方法,Dart 会按照从左到右的顺序调用,最后一个 mixin 的方法会覆盖前面的同名方法。
void main(List<String> args) {
var p = Xiaomi();
p.call(); // 最终调用的是最后一个 mixin 的 call 方法,即 Ios 的 call 方法
p.playStore();
p.appleStore();
}
mixin 不能构造函数
mixin class Android {
String version = 'Android 12';
// mixin 的构造函数不能有参数
// The class 'Android' can't be used as a mixin because it declares a constructor
// Android() {
// print('Android mixin constructor called');
// }
// Android(this.version);
Android();// 这个构造函数是合法的,因为它没有参数
void playStore() {
print('Google play store');
}
void call() {
print('Android phone is calling...');
}
}
为什么 mixin 不能有构造函数?
- Mixin 本质上是用来给类“混入”额外功能的代码块,而不是完整的类。
- 构造函数涉及对象的初始化和实例化,mixin 不负责实例化,因此不允许写构造函数。
- 如果你需要构造函数,应该用普通类继承(
extends
)而不是 mixin。
mixin on 限定条件
什么是 mixin on
限定条件?
on
关键字用于限定某个 mixin 只能被指定类型(类)或其子类混入。- 换句话说,
on
限定了 mixin 的“作用范围”,只有继承(或实现)了指定基类的类,才能使用这个 mixin。
正确使用
mixin class Phone {
void call() {
print('Phone is calling...');
}
}
// Android on Phone 表示 Android 是在 Phone 的基础上进行扩展
// 也可以理解为 Android 是 Phone 的一个子类
mixin Android on Phone {
void playStore() {
print('Google play store');
}
// 必须重新实现 call 方法么? // 不需要,因为 Android 已经继承了 Phone 的 call 方法
// 如果不想覆盖 Phone 的 call 方法,可以不实现 call 方法
// 如果想覆盖 Phone 的 call 方法,可以实现自己的 call 方法
@override
void call() {
super.call(); // 调用 Phone 的 call 方法
print('Android phone is calling...');
}
}
// 继承了Phone,然后再继承 Android
// 这样 Xiaomi 就可以使用 Phone 和 Android 的方法
class Xiaomi with Phone, Android {} // 这是合法的,因为 Android 是 mixin,可以和 Phone 一起使用
// 继承了 Phone,然后 mixin 了 Android
class Xiaomi4 extends Phone with Android {}
// 实现了 Phone , 然后 mixin 了 Android
class Xiaomi5 implements Phone, Android {
@override
void call() {
// TODO: implement call
}
@override
void playStore() {
// TODO: implement playStore
}
}
void main(List<String> args) {
var p = Xiaomi();
p.call(); // 调用 Android 的 call 方法
// Phone is calling...
// Android phone is calling..
}
class Xiaomi with Phone, Android {}
:正确,因为 Xiaomi 先混入了 Phone,再混入Android,所以符合 Android 的限制
class Xiaomi4 extends Phone with Android {}
:
- 继承了
Phone
,意味着Xiaomi4
是Phone
的子类,自动继承了Phone
的所有成员和实现。 - 使用了
with Android
混入了Android
的功能,Android
又限定了on Phone
,这里满足限制。 Android
可以调用super.call()
,调用的是Phone
的call()
。- 这是“继承 + mixin”的典型写法,既复用父类代码,也混入其他功能。
class Xiaomi5 implements Phone, Android { ... }
- 实现了接口
Phone
和Android
,必须实现它们声明的所有方法。 implements
不继承实现,只是承诺“实现接口的方法”,所以call()
和playStore()
都要自己写实现。- 适用于只关心接口规范,不想复用具体实现,或者需要自定义完全不同的实现。
两种错误示范
class Xiaomi2 with Android {}
Android
指明了on Phone
,它要求必须混入的类继承或实现Phone
。Xiaomi2
只混入了Android
,但它本身没有extends
或with
Phone
,这违反了Android
的限制条件。
class Xiaomi3 with Android, Phone {}
Android
是一个限定为on Phone
的 mixin,意思它只能被继承或混入了Phone
的类使用。但你这里先写了Android
,而后写了Phone
。- Dart 混入的顺序是从左到右:也就是说,
Xiaomi3
先混入了Android
,但此时它还没有继承或混入Phone
,所以编译器会报错说Android
的约束没满足。
继承链
在 Dart 里,类声明的语法是这样的:
class ClassName extends SuperClass with Mixin1, Mixin2, ... implements Interfaces {}
注意顺序:
extends
只能有一个,表示继承的父类;with
后面可以有多个mixin
,按从左到右依次混入。方法解析顺序 (MROMRO, Method Resolution Order):
当前类 → 第一个 mixin → 第二个 mixin → ... → 父类 → Object
如果我这么去写一个类:
class Sub with MixinA extends Dad {}
展开后的继承链是:
Sub → MixinA → Dad → Object
以下是一个完整的例子:
class Dad {
void say() => print("Dad");
}
mixin MixinA {
void say() => print("MixinA");
}
class Sub with MixinA extends Dad {
@override
void say() {
print("Sub");
super.say();
}
}
void main() {
Sub().say();
}
/*日志:
Sub
MixinA
*/
在 Sub
中写的 super.say()
,查找顺序是:
- 往继承链上一级(即
MixinA
)找say
方法; - 如果
MixinA
没有实现,就继续找Dad
; - 如果
Dad
也没有,就继续找Object
; - 找不到就报错。