Dart中类的类型

Dart是支撑根据mixin承继机制的面向目标言语,所有目标都是一个类的实例,而除了 Null以外的所有的类都承继自Object类。 根据mixin的承继意味着虽然每个类(top class Object? 除外)都只要一个超类,一个类的代码能够在其它多个类承继中重复运用。

以上这段是官方文档的说明,在实践运用中,由于mixin的加入,使得Dart中类的运用和其它言语有所不同。Dart中类的类型有三种,分别是:

  • class:声明一个类,供给详细的成员变量和办法完成。
  • abstract class:声明一个抽象类,抽象类将无法被实例化。抽象类常用于声明接口办法、有时也会有详细的办法完成。
  • mixin:声明一个Mixin类,与抽象类相同无法被实例化,是一种在多重承继中复用某个类中代码的办法形式,能够声明接口办法或有详细的办法完成。
  1. 每一个类都隐式地界说了一个接口并完成了该接口,这个接口包括所有这个类的成员变量以及这个类所完成的其它接口。
  2. 假如想让抽象类一起可被实例化,能够为其界说工厂结构函数。详细内容能够参阅:抽象类的实例化
  3. mixin关键字在Dart 2.1中才被引用支撑。前期版别中的代码一般运用 abstract class代替

从上述内容能够看出,mixin是后边才被引进的,与abstract class有些通用的当地,能够了解为abstract class的升级版。它相对于abstract class说,能够一起引进多个Mixin,而且能够经过on关键字来约束运用范围。

类相关关键字的运用

而对上述这些类型的运用,又有extends, with, implements, on这几个关键字:

  • extends:承继,和其它言语的承继没什么区别。
  • with:运用Mixin形式混入一个或者多个Mixin类
  • implements:完成一个或多个接口并完成每个接口界说的API。
  • on:约束Mixin的运用范围。

针对这几个关键字的运用,我做了一张表进行总结:

Dart中的extends, with, implements, on关键字详解

样例说明

针对上面的内容,我举几个比如,能够仿制代码到DartPad中进行验证:

类混入类或者抽象类(class with class)

class Animal {
  String name = "Animal";
}
abstract class Flyer {
  String name = "Flyer";
  void fly() => print('$name can fly!');
}
abstract class Eater extends Animal {
  void eat() => print('I can Eat!');
}
// 一起混入class和abstract class
abstract class Bird with Animal, Flyer {}
class Bird1 with Animal, Flyer {}
// 只支撑无任何承继和混入的类,Eater承继自Animal,所以它不支撑被混入。
// 报错:The class 'Eater' can't be used as a mixin because it extends a class other than 'Object'.
// class Bird with Eater { 
// }
main() {
  Bird1().fly();  // Flyer can fly!
}

类承继抽象类并混入Mixin

class Animal {
  String name = "Animal";
}
mixin Flyer {
  String name = "Flyer";
  void fly() => print('$name can fly!');
}
abstract class Eater extends Animal {
  @override
  String get name => "Eater";
  void eat() => print('$name can Eat!');
}
// 类承继抽象类并混入Mixin
class Bird extends Eater with Flyer {  }
main() {
  // 由于with(混入)的优先级比extends(承继)更高,所以打印出来的是Flyer而不是Eater
  Bird().fly(); // Flyer can fly!
  Bird().eat(); // Flyer can Eat!
}

类承继抽象类并混入Mixin的一起完成接口

class Biology {
  void breathe() => print('I can breathe');
}
class Animal {
  String name = "Animal";
}
// 这儿设置完成了Biology接口,可是mixin与abstract class相同并不要求完成接口,声明与完成均可。
// on关键字约束混入Flyer的类必须承继自Animal或它的子类
mixin Flyer on Animal implements Biology {
  @override
  String get name => "Flyer";
  void fly() => print('$name can fly!');
}
abstract class Eater extends Animal {
  @override
  String get name => "Eater";
  void eat() => print('$name can Eat!');
}
// 类承继抽象类并混入Mixin的一起完成接口
// 留意关键字的运用次序,依次是extends -> with -> implements
class Bird extends Eater with Flyer implements Biology {
  // 后边运用了`implements Biology`,所以子类必需求完成这个类的接口
  @override
  void breathe() => print('Bird can breathe!');
}
main() {
  // 由于with(混入)的优先级比extends(承继)更高,所以打印出来的是Flyer而不是Eater
  Bird().fly(); // Flyer can fly!
  Bird().eat(); // Flyer can Eat!
  Bird().breathe(); // Bird can breathe!
}

混入mixin的次序问题

abstract class Biology {
  void breathe() => print('I can breathe');
}
mixin Animal on Biology {
  String name = "Animal";
  @override
  void breathe() {
    print('$name can breathe!');
    super.breathe();
  }
}
mixin Flyer on Animal {
  @override
  String get name => "Flyer";
  void fly() => print('$name can fly!');
}
/// mixin的次序问题:
/// with后边的Flyer必须在Animal后边,不然会报错:
/// 'Flyer' can't be mixed onto 'Biology' because 'Biology' doesn't implement 'Animal'.
class Bird extends Biology with Animal, Flyer {
  @override
  void breathe() {
    print('Bird can breathe!');
    super.breathe();
  }
}
main() {
  Bird().breathe(); 
  /*
   * 上述代码履行,依次输出:
   * Bird can breathe!
   * Flyer can breathe!
   * I can breathe
   * */
}

这儿的次序问题和运行出来的成果会让人有点费解,可是能够这样了解:Mixin语法糖, 实质还是类承继. 承继能够复用代码, 但多承继会导致代码紊乱。 Java为了处理多承继的问题, 用了interface, 只要函数声明而没有完成(后边加的default也算语法糖了)。以A with B, C, D为例,实践是A extends D extends C extends B, 所以上面的Animal必须在Flyer前面,不然就变成了Animal extends Flyer,会呈现儿子给爹当爹的紊乱问题。

mixin的底层实质只是猜测,并没有查看言语底层源码进行验证.

总结

从上述样例能够看出,三种类结构能够一起存在,关键字的运用有前后次序:extends -> mixins -> implements。 另外需求留意的是相同办法的优先级问题,这个有两种状况:

  1. 一起被extendswithimplements时,混入(with)的优先级比承继(extends)要高,而implements只供给接口,不会被调用。
  2. with多个Mixin时,则会调用间隔with关键字最远Mixin中的办法。

当然,假如当时运用类重写了该办法,就会优先调用当时类中的办法。

参阅资料

  • Dart官方文档
  • Dart之Mixin详解
  • Flutter 基础 | Dart 语法 mixin:对mixin的运用场景进行了很好的说明

by: 星的天空