mixin 是 Dart 中非常重要的概念,对于未接触过此概念的Coder来说尤其重要,最近看源码的时候,由于对 mixin 不熟悉导致理解出现偏差,走了很多弯路,所以这篇文章介绍一下 mixin 概念。

Dart 及 Engine 版本:

Engine • revision ae90085a84

Tools • Dart 2.10.4

请注意版本,不同的版本可能存在差异。

先来看下官方的定义:

Mixins are a way of reusing a class’s code in multiple class hierarchies.

Mixins 是一种在多个类层次结构中重用类代码的方法。

在来看下 Wiki 的解释:

In object-oriented programming languages, a mixin (or mix-in) is a class that contains methods for use by other classes without having to be the parent class of those other classes. How those other classes gain access to the mixin's methods depends on the language. Mixins are sometimes described as being "included" rather than "inherited".

Mixins encourage code reuse and can be used to avoid the inheritance ambiguity that multiple inheritance can cause (the "diamond problem"), or to work around lack of support for multiple inheritance in a language. A mixin can also be viewed as an interface with implemented methods. This pattern is an example of enforcing the dependency inversion principle.

翻译如下:

在面向对象的编程语言中,mixin(或mix-in)是一个类,其中包含供其他类使用的方法,而不必成为其他类的父类。 这些其他类如何获得对mixin方法的访问权限取决于语言。 混合素有时被描述为“包含”而不是“继承”。

Mixins鼓励代码重用,并且可用于避免多重继承可能导致的继承歧义(“钻石问题”),或解决语言中对多重继承的支持不足的问题。 混合也可以看作是已实现方法的接口。 此模式是强制执行依赖关系反转原理的示例。

看完这两段介绍,可能依然对其比较模糊,不要紧,现在只需对其有个概念即可,下面会详细介绍 Mixins 的用法,我个人的理解就是:Mixins 解决了无法多重继承的问题。

什么时候需要使用 Mixins

有如下场景:

定义一个基类人(Person),它有吃(eat)的方法。

有3个实际的人A、B、C,它们都继承 Person,但是3个人有不同的技能:

  • A :会唱歌、跳舞
  • B:会跳舞、写代码
  • C:会唱歌、写代码

上面的场景中唱歌、跳舞、写代码是一种技能,并不是每一个人都会的,所以将其定义在 Person 中是不合适的,如果各自定义为一个类,又不能同时继承Person和唱歌、跳舞、写代码,如果将唱歌、跳舞、写代码定义为 Interface ,那么A、B、C中要各自实现其方法,

那要如何实现呢? Mixins 出场啦。

定义一个 Person 基类和功能类唱歌、跳舞、写代码:

class Person {
eat() {
print('Person eat');
}
} class Dance {
dance() {
print('Dance dance');
}
} class Sing {
sing() {
print('Sing sing');
}
} class Code {
code() {
print('Code code');
}
}

定义A、B、C:

class A extends Person with Dance, Sing {}

class B extends Person with Sing, Code {}

class C extends Person with Code, Dance {}

注意:混合使用 with 关键字。

使用:

A a = A();
a.eat();
a.dance();
a.sing();

输出日志:

flutter: Person eat
flutter: Dance dance
flutter: Sing sing

可以看到 A 中有了Dance 和Sing的相关的方法。

Dance 是一个 class,如果给其添加构造函数会如何?

给 Dance 添加构造函数,修改如下,

此时发现 A 和 C 无法编译,出现如下错误:

很明显,需要 mixin 的类无法定义构造函数。

所以一般会将需要 mixin 的类使用 mixin 关键字:

添加限定条件,使用关键字 on

接着上面的场景继续,这时定义一个狗的类,目前狗这个类也可以混合 Dance 、Sing 和 Code,

class Dog with Code{}

但是,Code 是人类独有的技能,不希望 Dog 这个类可以mixin,所以给 Code 添加限定条件:

使用关键字 on 限定Code 只能被 Person 或者其子类 mixin。

此时 Dog 无法 mixin Code。

添加限定后,可以重写其方法, Code 重写 Person 的方法:

super 表示调用父类(Person)的方法。

如何处理多个类有同一方法的情况

假设有D 和 D1 两个类,有同一个方法 d,E mixin D 和 D1:

此时,调用 e.d 方法:

E e = E();
e.d();

输出:

flutter: D1 d

说明后面的将前面的覆盖了,调换下D 和 D1的顺序:

class E with D1, D {}

输出:

flutter: D d

此时在 E 中也添加 d 方法:

输出:

flutter: E d

说明 E 中 方法覆盖了原来的。

E 中 d 方法可以调用 super.d()

输出:

flutter: D d
flutter: E d

假设现在有F、G、H 三个类,都有 a 方法,

有如下定义的类:

那么下面会输出什么值:

答案是:

flutter: G a

记住:混合类时,进行混合的多个类是线性的,这是他们共有方法不冲突的原因,混合的顺序非常重要,因为它决定了混合时相同的方法的处理逻辑。

再次看下 FG 的混合情况:

FG 继承 H,混合 F 和 G,对于相同方法的优先级为:G > F > H,因此共有方法 a,最后执行的是 G 类中的 a 方法。

那么如果 FG 中也有 a 方法会如何?

如果本身(FG)也存在相同的方法那么优先级:FG > G > F > H。super.a() 执行的是 G 中的 a 方法。

输出结果:

flutter: G a
flutter: FG a

更复杂的来啦,请看如下混合关系:

BB 为一个抽象类,有一个构造函数,其中执行 init 方法,GB 和 PB 为一个混合类型,限定了只有 BB 或者其子类才能混合,WFB 继承 BB,并混合GB、PB,此时创建 WFB 对象,

WFB wfb = WFB();

输出结果是什么?

flutter: BB Constructor
flutter: BB init
flutter: GB init
flutter: PB init

是不是很诧异,按照上面的逻辑不是应该只调用 PB 的 init 方法吗?

你理解的没有错,的确只调用了PB 的 init 方法,但是 PB 的 init 方法中调用了super.init(),这个才是重点,PB 通过 super.init 调用到了GB中的 init 方法, GB 通过 super.init 调用到了 BB 中的 init 方法,所以最终输出的就是上面的结果。

这个一定要理解其中的调用顺序,因为的 Flutter Framework 的入口函数 runApp 中就是此形式:

WidgetsFlutterBinding.ensureInitialized 方法如下:

WidgetsFlutterBinding 混合结构如下:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, SchedulerBinding, ServicesBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

BindingBase 及构造函数如下:

其执行了 initInstances 和 initServiceExtensions 方法。看下面混合的顺序:

从后到前依次执行其 initInstances 和 initServiceExtensions(如果有) 方法,由于 initInstances 和 initServiceExtensions 方法中首先执行 super.initInstances()super.initServiceExtensions() ,所以最后执行的顺序为:BindingBase -> GestureBinding -> SchedulerBinding -> ServicesBinding -> PaintingBinding -> SemanticsBinding -> RendererBindinsg -> WidgetsBinding 。

类型

还是上面的F、G、H 三个类,那么 FG 的类型是什么,看下面的判断会输出什么?

输出:

flutter: FG is F : true
flutter: FG is G : true
flutter: FG is H : true

所以混合后的类型是超类的子类型。

总结

  1. Mixins 使我们可以在无需继承父类的情况下为此类添加父类的“功能”,可以在同一个类中具有一个父级和多个 mixin 组件。
  2. Mixins 不可以声明任何构造函数。
  3. Mixins 添加限定条件使用 on 关键字。
  4. 混合使用 with 关键字,with 后面可以是 classabstract classmixin 的类型。
  5. Mixins 不是多重继承,相反,它只是在多个层次结构中重用类中的代码而无需扩展它们的一种方式。

交流

老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com

添加微信或者公众号领取 《330个控件大全》和 《Flutter 实战》PDF。

欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:

Flutter 中不得不会的 mixin的更多相关文章

  1. Flutter中mixin的使用

    页表页面 这是一个普通的展示数据,上拉加载更多数据的列表. 其中有一个类型为List<T>的数据列表listData,有个page数据用于分页,isLoading用来判断是否正在加载数据, ...

  2. Flutter 中 JSON 解析

    本文介绍一下Flutter中如何进行json数据的解析.在移动端开发中,请求服务端返回json数据并解析是一个很常见的使用场景.Android原生开发中,有GsonFormat这样的神器,一键生成Ja ...

  3. Flutter 中如何优雅的实现多渠道打包(埋点统计系列)

    我是 Zero,脑图先奉上 先赞后看,更新永不断 只要你关注 Flutter,这篇文章你绝对用得着,==> 强烈建议收藏 多渠道打包介绍 多渠道打包的主要作用是满足产品的运营需求,统计渠道和活动 ...

  4. 在Flutter中嵌入Native组件的正确姿势是...

    引言 在漫长的从Native向Flutter过渡的混合工程时期,要想平滑地过渡,在Flutter中使用Native中较为完善的控件会是一个很好的选择.本文希望向大家介绍AndroidView的使用方式 ...

  5. 从零学习Fluter(五):Flutter中手势滑动拖动已经网络请求

    从六号开始搞Flutter,到今天写这篇blog已经过了4天时间,文档初步浏览了一遍,写下了这个demo.demo源码分享在github上,现在对flutter有种说不出的喜欢了.大家一起搞吧! 废话 ...

  6. mvp 在 flutter 中的应用

    在 Android 应用程序开发过程中,我们经常会用到一些所谓的架构方法,如:mvp,mvvm,clean等.之所以这些方法会被推崇是因为他们可以大大的解耦我们的代码的功能模块,让我们的代码在项目中后 ...

  7. Flutter 中文文档网站 flutter.cn 正式发布!

    在通常的对 Flutter 介绍中,最耳熟能详的是下面四个特点: 精美 (Beautiful):充分的赋予和发挥设计师的创造力和想象力,让你真正掌控屏幕上的每一个像素. ** 极速 (Fast)**: ...

  8. 理解 Flutter 中的 Key

    概览 在 Flutter 中,大概大家都知道如何更新界面视图: 通过修改 Stata 去触发 Widget 重建,触发和更新的操作是 Flutter 框架做的. 但是有时即使修改了 State,Flu ...

  9. flutter 中的样式

    flutter 中的样式 样式 值 width 320.0 height 240.0 color Colors.white,Colors.grey[300] textAlign TextAlign.c ...

随机推荐

  1. Spark剖析-宽依赖与窄依赖、基于yarn的两种提交模式、sparkcontext原理剖析

    Spark剖析-宽依赖与窄依赖.基于yarn的两种提交模式.sparkcontext原理剖析 一.宽依赖与窄依赖 二.基于yarn的两种提交模式深度剖析 2.1 Standalne-client 2. ...

  2. linux上 用户间发送消息 通信

    联想:scp命令 1.使用write命令,向指定用户发送信息:           用户可以使用write命令给其他在线用户发送消息.格式: $ write zhangsan hello   worl ...

  3. Java开发中POJO和JSON互转时如何忽略隐藏字段

    1. 前言 在Java开发中有时候某些敏感信息我们需要屏蔽掉,不能被消费这些数据的客户端知道.通常情况下我们会将其设置为null或者空字符 "",其实还有其它办法,如果你使用了Ja ...

  4. 考研机试练习(KY2-KY10)

    KY2 成绩排序 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M 本题知识点: 排序 sort struct 题目描述 查找和排序 题目:输入任意(用户,成绩) ...

  5. Hiho1422 Harmonic Matrix Counter (高斯消元)

    16年北京站A题 真的难啊.. 题意: 定义和谐矩阵 就是每个元素和上下左右的xor值=0 输出一个超大数 然后最多800个询问 求字典序第k小的和谐矩阵 x y位置上的数 题解: 首先这个超大数的范 ...

  6. HDU5740 Glorious Brilliance【最短路 KM匹配】

    HDU5740 Glorious Brilliance 题意: 给出一张不一定合法的染色图,每次可以交换相邻两点的颜色,问最少多少次能使染色图合法 合法的染色图相邻点的颜色不能相同 题解: 首先要确定 ...

  7. 【noi 2.6_9280】&【bzoj 1089】严格n元树(DP+高精度+重载运算符)

    题意:定义一棵树的所有非叶节点都恰好有n个儿子为严格n元树.问深度为d的严格n元树数目. 解法:f[i]表示深度为<=i的严格n元树数目.f[i]-f[i-1]表示深度为i的严格n元树数目.f[ ...

  8. CodeForces - 916C 思维

    题意:给你n,m,表示n个顶点和m条边,让你构造一个图. 要求 1.1->n最短路为素数 2.最小生成树边权和为prime 3.没有重边 4.边大小[1,1e9]. (题目给定m>n-1) ...

  9. WPF 之路由事件和附加事件(六)

    一.消息驱动与直接事件模型 ​ 事件的前身是消息(Message).Windows 是消息驱动的系统,运行其上的程序也遵循这个原则.消息的本质就是一条数据,这条消息里面包含着消息的类别,必要的时候还记 ...

  10. K8S(03)核心插件-Flannel网络插件

    系列文章说明 本系列文章,可以基本算是 老男孩2019年王硕的K8S周末班课程 笔记,根据视频来看本笔记最好,否则有些地方会看不明白 需要视频可以联系我 K8S核心网络插件Flannel 目录 系列文 ...