Dart中的extends, with, implements, on关键字详解
Dart中类的类型
Dart
是支持基于mixin
继承机制的面向对象语言,所有对象都是一个类的实例,而除了 Null
以外的所有的类都继承自Object
类。 基于mixin
的继承意味着尽管每个类(top class Object? 除外)都只有一个超类,一个类的代码可以在其它多个类继承中重复使用。
以上这段是官方文档的说明,在实际使用中,由于mixin
的加入,使得Dart
中类的使用和其它语言有所不同。Dart中类的类型有三种,分别是:
- class
:声明一个类,提供具体的成员变量和方法实现。
- abstract class
:声明一个抽象类,抽象类将无法被实例化。抽象类常用于声明接口方法、有时也会有具体的方法实现。
- mixin
:声明一个Mixin
类,与抽象类一样无法被实例化,是一种在多重继承中复用某个类中代码的方法模式,可以声明接口方法或有具体的方法实现。
- 每一个类都隐式地定义了一个接口并实现了该接口,这个接口包含所有这个类的成员变量以及这个类所实现的其它接口。
- 如果想让抽象类同时可被实例化,可以为其定义
工厂构造函数
。具体内容可以参考:抽象类的实例化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
的使用范围。
针对这几个关键字的使用,我做了一张表进行总结:
样例说明
针对上面的内容,我举几个例子,可以复制代码到DartPad中进行验证:
类混入类或者抽象类(class with class)
```dart 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
```dart 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的同时实现接口
```dart 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的顺序问题
```dart 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. 同时被extends
,with
,implements
时,混入(with
)的优先级比继承(extends
)要高,而implements
只提供接口,不会被调用。
2. with
多个Mixin
时,则会调用距离with
关键字最远Mixin
中的方法。
当然,如果当前使用类重写了该方法,就会优先调用当前类中的方法。
参考资料
- Dart官方文档
- Dart之Mixin详解
- Flutter 基础 | Dart 语法 mixin:对
mixin
的使用场景进行了很好的说明
by: 星的天空