Flutter 中不得不会的 mixin-LMLPHP

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

Dart 及 Engine 版本:

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

先来看下官方的定义:

在来看下 Wiki 的解释:

翻译如下:

看完这两段介绍,可能依然对其比较模糊,不要紧,现在只需对其有个概念即可,下面会详细介绍 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 添加构造函数,修改如下,

Flutter 中不得不会的 mixin-LMLPHP

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

Flutter 中不得不会的 mixin-LMLPHP

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

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

Flutter 中不得不会的 mixin-LMLPHP

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

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

class Dog with Code{}

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

Flutter 中不得不会的 mixin-LMLPHP

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

Flutter 中不得不会的 mixin-LMLPHP

此时 Dog 无法 mixin Code。

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

Flutter 中不得不会的 mixin-LMLPHP

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

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

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

Flutter 中不得不会的 mixin-LMLPHP

此时,调用 e.d 方法:

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

输出:

flutter: D1 d

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

class E with D1, D {}

输出:

flutter: D d

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

Flutter 中不得不会的 mixin-LMLPHP

输出:

flutter: E d

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

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

Flutter 中不得不会的 mixin-LMLPHP

输出:

flutter: D d
flutter: E d

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

Flutter 中不得不会的 mixin-LMLPHP

有如下定义的类:

Flutter 中不得不会的 mixin-LMLPHP

那么下面会输出什么值:

Flutter 中不得不会的 mixin-LMLPHP

答案是:

flutter: G a

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

再次看下 FG 的混合情况:

Flutter 中不得不会的 mixin-LMLPHP

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

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

Flutter 中不得不会的 mixin-LMLPHP

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

输出结果:

flutter: G a
flutter: FG a

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

Flutter 中不得不会的 mixin-LMLPHP

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 中就是此形式:

Flutter 中不得不会的 mixin-LMLPHP

WidgetsFlutterBinding.ensureInitialized 方法如下:

Flutter 中不得不会的 mixin-LMLPHP

WidgetsFlutterBinding 混合结构如下:

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

BindingBase 及构造函数如下:

Flutter 中不得不会的 mixin-LMLPHP

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

Flutter 中不得不会的 mixin-LMLPHP

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

类型

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

Flutter 中不得不会的 mixin-LMLPHP

输出:

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】:

02-23 02:21