当前位置:网站首页>【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
边栏推荐
- 分享一个适用于MCU项目的代码框架
- 解除360对默认浏览器的检测与修改
- XSS range intermediate bypass
- Interpretation of the meaning of each dimension of two-dimensional, three-dimensional, and four-dimensional matrices
- SIPp 安装及使用
- Does LabVIEW really close the COM port using VISA Close?
- 17、负载均衡
- Pytorch模型训练实用教程学习笔记:三、损失函数汇总
- 我的驾照考试笔记(4)
- 【kali-信息收集】(1.4)识别活跃的主机/查看打开的端口:Nmap(网络映射器工具)
猜你喜欢
【节能学院】安科瑞餐饮油烟监测云平台助力大气污染攻坚战
图文详述Eureka的缓存机制/三级缓存
KDD2022 | Self-Supervised Hypergraph Transformer Recommendation System
Creo5.0 rough hexagon is how to draw
Arthas 常用命令
【kali-信息收集】(1.4)识别活跃的主机/查看打开的端口:Nmap(网络映射器工具)
【社媒营销】如何知道自己的WhatsApp是否被屏蔽了?
【节能学院】数据机房中智能小母线与列头柜方案的对比分析
【kali-信息收集】(1.6)服务的指纹识别:Nmap、Amap
LabVIEW 使用VISA Close真的关闭COM口了吗
随机推荐
【个人作品】无线网络图传模块
【Social Media Marketing】How to know if your WhatsApp is blocked?
互联网大厂研发流程
Greenplum数据库源码分析——Standby Master操作工具分析
Software you should know as a programmer
The graphic details Eureka's caching mechanism/level 3 cache
【kali-信息收集】(1.4)识别活跃的主机/查看打开的端口:Nmap(网络映射器工具)
第55章 业务逻辑之订单、支付实体定义
Little data on how to learn?Jida latest small learning data review, 26 PDF page covers the 269 - page document small data learning theory, method and application are expounded
面试突击70:什么是粘包和半包?怎么解决?
【torch】张量乘法:matmul,einsum
Ruijie switch basic configuration
Redis does web page UV statistics
{ValueError}Number of classes, 1, does not match size of target_names, 2. Tr
有用的网站
数据可视化
图文详述Eureka的缓存机制/三级缓存
XSS靶场中级绕过
作为程序员你应该会的软件
Does LabVIEW really close the COM port using VISA Close?