Flutter 中不得不会的 mixin

mixin 是 Dart 中很是重要的概念,对于未接触过此概念的Coder来讲尤为重要,最近看源码的时候,因为对 mixin 不熟悉致使理解出现误差,走了不少弯路,因此这篇文章介绍一下 mixin 概念。git

Dart 及 Engine 版本:编程

Engine • revision ae90085a84
Tools • Dart 2.10.4微信

请注意版本,不一样的版本可能存在差别。编程语言

先来看下官方的定义:函数

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

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

在来看下 Wiki 的解释:3d

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.code

翻译以下:

在面向对象的编程语言中,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】:

相关文章
相关标签/搜索