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)
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
。
另外需要注意的是相同方法的优先级问题,这个有两种情况:
- 同时被
extends
,with
,implements
时,混入(with
)的优先级比继承(extends
)要高,而implements
只提供接口,不会被调用。 with
多个Mixin
时,则会调用距离with
关键字最远Mixin
中的方法。
当然,如果当前使用类重写了该方法,就会优先调用当前类中的方法。
参考资料
- Dart官方文档
- Dart之Mixin详解
- Flutter 基础 | Dart 语法 mixin:对
mixin
的使用场景进行了很好的说明
作者:星的天空
链接:https://juejin.cn/post/7094642592880525320
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。