当前位置:网站首页>【Dart】dart之mixin探究
【Dart】dart之mixin探究
2022-08-01 20:04:00 【allanGold】
由于dart中是没有interface的,在dart中我们需要定义接口的话用的是class关键字
implements是把某个class当做接口来实现要求我们重写这个class的所有方法
注意:implements会将class的实现抹掉就不存在默认实现一说,而dart也是不允许多继承的。
extends表示继承某个class,可以继承父类实现了的方法
mixin实际上也是面向对象编程中的概念,用户与Mixin不是“is-a”的关系,而是“-able”关系
Mixin是面向对象程序设计语言中的类,提供了方法的实现。其他类可以访问mixin类的方法而不必成为其子类。[1]Mixin有时被称作"included"而不是"inherited"。mixin为使用它的class提供额外的功能,但自身却不单独使用(不能单独生成实例对象,属于抽象类)。因为有以上限制,Mixin类通常作为功能模块使用,在需要该功能时“混入”,而且不会使类的关系变得复杂。
dart语言里面我们可以使用with关键字实现mixin,将一个或者多个class混入另一个类:
class Base1 {
void foo1() {
print("foo1");
}
}
class Base2 {
void foo2() {
print("foo2");
}
}
class Child2 with Base1, Base2 {
}
没错,通过with多个类,可以实现类似多继承的效果。
既然允许with多个类,那么如果这些类中有个相同方法,那会出现什么事情?
实际上kotlin、java8使用接口的默认实现也会出现一样的问题,他们的处理方法是当出现相同方法的时候实现类需要手动指定使用哪个接口的默认实现,要不然编译会报错:
// java8
interface IBase1 {
default void foo() {
System.out.println("1");
}
}
interface IBase2 {
default void foo() {
System.out.println("2");
}
}
class Child implements IBase1, IBase2 {
@Override
public void foo() {
IBase2.super.foo();
}
}
//kotlin
interface IBase1 {
fun foo() {
println("1")
}
}
interface IBase2 {
fun foo() {
println("2")
}
}
class Child : IBase1, IBase2 {
override fun foo() {
super<IBase2>.foo()
}
}
必须提到的是:无论是 extends、implements 还是 mixin,优先级最高的是在具体类中的方法。
线性化
而在dart中with里面越后面的类优先级越高:
class Base1 {
void foo() {
print("1");
}
}
class Base2 {
void foo() {
print("2");
}
}
class Base3 {
void foo() {
print("3");
}
}
class Child extends Base1 with Base2, Base3 {}
这个时候调用Child.foo方法实际会优先调用Base3.foo。原因是dart实际是通过创建中间类继承实现的mixin,上面的代码相当于:

通过从左到右的顺序生成中间父类去继承将extends、with线性化成一个单继承链。所以Base2、Base3实际上不是Child的父类
mixin关键字
在上面的例子中我们使用普通的class去with,但dart实际上提供了一个mixin关键字,它定义了不能实例化、也不能extends只能with的类:
mixin Base {
}
// 编译失败: mixin类不能extends
// class Child extends Base {
//
// }
// 编译成功: mixin类可以with
class Child with Base {
}
void main() {
// 编译失败: mixin类不能实例化
// Base()
}
这样的类实际上和java、kotlin里面的interface已经很像了。
另外我们可以通过mixin … on 限定某个类只能由某些类去with:
class Base1 {
void foo() {
print("1");
}
}
class Base2 {
void foo() {
print("2");
}
}
mixin Base3 on Base1 {
void foo() {
super.foo();
print("3");
}
}
class Child extends Base1 with Base2, Base3 {}
上面的demo中Base3只能由Base1去with,那就以为着这个with Base3的类一定是继承或者with了 Base1,所以可以调用这个类的super.foo方法。要注意的是,这个super.foo并不指定一定调用的是Base1.foo。例如上面的代码调用Child().foo()之后的打印实际上是:
2
3
它们线性化的到的继承关系和前面全是class的代码并没有差别:

从上面的uml图我们就能理解为什么打印是2 3了
理解了这个简单的例子之后我们再来看一个复杂一点的例子:
class Base1 {
void foo1() {
print("Base1.foo1");
foo2();
}
void foo2() {
print("Base1.foo2");
}
}
mixin Base2 on Base1 {
void foo1() {
super.foo1();
print("Base2.foo1");
}
void foo2() {
print("Base2.foo2");
}
}
class Child with Base1,Base2 {}
void main() {
Child().foo1();
}
它的输出是:
Base1.foo1
Base2.foo2
Base2.foo1
原因是Base2.foo1中的super.foo1实际上调用的是Base1.foo1,而Base1.foo1中的foo2,由于继承的多态特性,调用的是Base2.foo2。
我们可以通过下面uml图辅助理解,注意看继承关系里面是没有Base1、Base2的因为它们都是通过with混入的,并不是Child的父类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fLZsiSq3-1659324422994)(https:upload-images.jianshu.io/upload_images/2199790-7482262610cc24c7.png?imageMogr2/auto-orient/strip|imageView2/2/w/758)]
with的类不能有构造函数
另外,with的class和mixin类型都是不允许有构造函数的,因为mixin机制语义上是向一个类混入其他类的方法或者成员变量,使得我们可以在混合类中访问到混入类的方法或者属性。而混入其他类的构造函数实际上是没有意义的,因为不会有人手动去调用这个混入类的构造函数。
class Base1 {
Base1() {
}
}
// 编译失败: 不能with一个带有构造函数的类
// class Child with Base1 {}
// 编译失败: mixin类型只能with,所以不能有构造函数
// mixin Base2 {
// Base2() {}
// }
参考博客
https://medium.com/flutter-community/dart-what-are-mixins-3a72344011f3
https://www.jianshu.com/p/f4efaa6b8fe6
https://www.jianshu.com/p/fc96bef9beba
https://my.oschina.net/zzxzzg/blog/2962518
边栏推荐
- 八百客、销售易、纷享销客各行其道
- 不同的操作加不同的锁详解
- Redis 做签到统计
- Failed to re-init queues : Illegal queue capacity setting (abs-capacity=0.6) > (abs-maximum-capacity
- 【kali-信息收集】(1.4)识别活跃的主机/查看打开的端口:Nmap(网络映射器工具)
- 57:第五章:开发admin管理服务:10:开发【从MongoDB的GridFS中,获取文件,接口】;(从GridFS中,获取文件的SOP)(不使用MongoDB的服务,可以排除其自动加载类)
- WhatsApp group sending actual combat sharing - WhatsApp Business API account
- 油猴hook小脚本
- 【Untitled】
- Pytorch模型训练实用教程学习笔记:四、优化器与学习率调整
猜你喜欢
随机推荐
Custom command to get focus
Batch get protein .pdb files based on Uniprot ID/PDB ID
模板特例化和常用用法
datax - 艰难debug路
卷积神经网络(CNN)mnist数字识别-Tensorflow
【Untitled】
【节能学院】推进农业水价综合改革的意见解读
win10,在proe/creo中鼠标中键不能放大缩小
Debug一个ECC的ODP数据源
Oracle排序某个字段, 如果这个varchar2类型的字段有数字也有文字 , 怎么按照数字大小排序?
使用Huggingface在矩池云快速加载预训练模型和数据集
latex论文神器--服务器部署overleaf
【kali-信息收集】(1.3)探测网络范围:DMitry(域名查询工具)、Scapy(跟踪路由工具)
How PROE/Croe edits a completed sketch and brings it back to sketching state
Creo5.0 rough hexagon is how to draw
Remove 360's detection and modification of the default browser
LTE时域、频域资源
30-day question brushing plan (5)
分享一个适用于MCU项目的代码框架
我的驾照考试笔记(1)









