前言
Dart 是一门面向对象的语言, 所有的类都是继承自 Object
, 除了支持传统的 继承、封装、多态
, 还有基于组合(Mixin-based)的继承特性, 这个特性也是本文将会重点介绍的地方
既然是面向对象的语言, 先让我们来看下如何定义一个类:
class ClassName{
}
通过 class
关键字定义类, 语法和其他主流的语言没有什么差别
有过使用面向对象语言经验的开发者知道, 一般类由成员变量, 成员方法, 构造方法, 静态变量, 静态方法
组成
成员变量和静态变量
下面我们来看下如何声明成员变量:
class Point {
// 声明成员变量 x
num x;
// 声明成员变量 y
num y;
}
成员变量默认会生成一个隐式的 getter
方法, 并且 非final
的成员变量也会隐式的生成 setter
方法
除此以外, 开发者还可以使用 get
和 set
关键字实现 getter
和 setter
方法来创建新的属性, 如:
class Rectangle {
num left, top, width, height;
// 构造函数
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个属性: right and bottom.
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
声明静态变量, 在前面声明成员变量的基础上加上 static
关键字:
class Person{
// 静态变量
static var count = 0;
// 静态常量
//static const var count = 0;
}
成员方法和静态方法
如果定义函数已经在 (三)Flutter学习之Dart函数 介绍过了
类的成员方法就是把以前介绍的方法定义到类里面而已
class Person {
void sayHello() {
print("hello...");
}
}
void main() {
var p = Person();
p.sayHello();
}
类的静态方法, 就是在成员方法基础上加上 static
关键字, 访问的时候只需要类名即可而不需要对象, 这个和 Java 是一样的
class Person {
static void printDesc() {
print("this is person description...");
}
}
void main() {
Person.printDesc();
}
构造方法
在类中创建一个和类名一样的函数这就是构造函数, 如:
class Point {
// 成员变量
num x, y;
// 构造方法
Point(num x, num y) {
this.x = x;
this.y = y;
}
}
Dart 为我们提供了语法糖来简化上面的代码:
class Point {
num x, y;
// 声明构造方法, 接受两个参数, 并且将参数分别赋值给对应的变量上
Point(this.x, this.y);
}
Dart 还为我们提供了 Named constructor
, 让代码可读性更高, 让开发者通过名字就知道该构造方法是干什么的
class Person {
String firstName;
// 声明 Named constructor
Person.fromJson(Map data) {
print('in Person');
}
}
void main() {
var p = Person.fromJson({});
}
如果想将构造方法声明为 private 的, 加上下划线即可:
// 私有构造函数, 只能在当前类可用
Person._fromJson(Map data) {
print('in Person');
}
构造方法还可以调用另一个构造方法:
class Point {
num x, y;
Point(this.x, this.y);
// 通过 this 关键字调用其他构造函数
Point.alongXAxis(num x) : this(x, 0);
}
除了在构造方法中调用其他的构造方法, 还可以调用父类的构造方法:
class Employee extends Person {
// 通过 super 关键字调用父类的构造方法
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
初始化器列表(Initializer list)
Dart 还支持 初始化器列表(Initializer list)
, 它在构造方法体执行之前执行, 在构造方法后用 冒号(:)
分隔初始化器列表
, 例如:
Point.fromJson(Map json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
由于初始化器列表
初始化成员变量操作是在构造方法体执行前执行, 所以初始化器列表
中不能使用 this
关键字
在开发期间还可以在初始化器列表
中使用断言:
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
如果某个类创建对象后不再发生更改, 可以将对象声明为运行时常量, 将对象声明为常量, 需要将类的构造方法声明为常量构造方法
并且所有的成员变量都必须是 final
的
class ImmutablePoint {
final num x, y;
const ImmutablePoint(this.x, this.y);
}
如果创建两个常量对象, 实际上他们是一个对象实例:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
// They are the same instance!
assert(identical(a, b));
工厂构造器(Factory Contructor)
工厂构造器就构造方法前面使用 factory
关键字修饰:
factory Person(String name){
}
其实这不是严格意义上的构造函数, 在其方法体内不能访问 this
, 只是为了调用该方法的时候就像调用普通的构造方法一样
而不用关心到底是返回了一个新的对象还是缓存的对象. 例如下面一个单例模式的代码:
class Singleton {
static final Singleton _singleton = new Singleton._internal();
factory Singleton() {
return _singleton;
}
// 将默认的构造函数声明为private
Singleton._internal();
}
main() {
// 就像调用普通的构造方法一样
var s1 = Singleton();
var s2 = Singleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true
}
抽象类(Abstract classes)
抽象类使用 abstract
修饰符来修饰, 抽象类不能被实例化, 如果抽象类里的方法没有方法体, 那么表示该方法是抽象方法:
abstract class AbstractContainer {
// 抽象方法
void updateChildren();
List getChildren(){
return container;
}
}
接口(interfaces)
定义接口并没有特殊的关键字, 使用 class 关键字来定义接口, 只不过里面的方法都是没有方法体的抽象方法
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(String who) => 'Hello, $who. I am $_name.';
}
实现接口的时候需要使用 implements
关键字:
class Point implements Comparable, Location {...}
类的继承
不管是继承抽象类还是普通类, 使用 extends
关键字
如果重载父类的方法时, 需要调用父类的方法, 使用 super 关键字
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
}
class SmartTelevision extends Television {
@override
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
}
extends VS implements
通过上面介绍我们知道继承一个类使用 extends
关键字, 实现一个接口使用 implements
关键字
如果使用 implements
关键字实现一个非接口的类, 如:
class A {}
class B implements A{}
在 Dart
中这是允许的, 在 Java
中 implements
关键后面只能放接口
如果 implements
一个非接口类, 需要实现该类的里面的所有方法:
class A {
void sayHello(){
print("My name is A");
}
}
class B implements A{
@override
void sayHello() {
print("My name is B");
}
}
mixin 特性
Dart 中的 mixin 特性主要是用来多个层级类的代码重用
Dart 和 Java 一样都是单继承的,也就是说如果要复用多个类的代码,通过继承的方式是行不通的
在 Dart 中通过 with 关键字来实现 mixin 特性:
class Fly{
void fly(){
print("flying");
}
}
class Animal{
}
class Bird extends Animal with Fly{
}
main(){
Bird().fly();
}
从中可以看出,我们可以通过非继承的方式来使用 Fly
类的功能
Bird
除了具有 fly
的功能,还有 run
的功能, 我们通过 mixin
多个类来实现:
class Run {
void run() {
print("running");
}
}
class Bird extends Animal with Fly, Run {}
main() {
var bird = Bird();
bird.fly();
bird.run();
}
能够被 mixin 的类需要满足一定的条件:
- 不能有包含默认构造函数
- 只能继承自 Object
上面的 Bird
继承了 Animal
, 然后 mixin Fly
这个类, 如果 Animal
中也有 fly
方法它会选择执行哪个 fly
方法呢
class Animal {
void fly() {
print("Animal flying");
}
}
经过测试发现会使用 Fly
类里的 fly
方法, 也就是说 mixin
Class 比 extends
Class 优先级要高
如果我们 mixin 多个类, 在这多个类中包含了相同的方法, Dart会选择执行哪个呢?
class FlyEnhance{
void fly() {
print("fly enhance");
}
}
class Bird extends Animal with Run, Fly, FlyEnhance {}
main() {
var bird = Bird();
bird.fly();
bird.run();
}
//输出结果:
fly enhance
running
类 Fly, FlayEnhance
中都有 fly
方法, 经测试发现, 使用哪个类的 fly
方法取决于 with
后面顺序, 后面的 FlyEnhance
会覆盖前面的 Fly
中的 fly 方法
mixin 多个类的原理分析
上面的代码:
class Bird extends Animal with Run, Fly, FlyEnhance {}
底层会创建多个类来实现这个功能, 相当于下面的代码:
class $AnimalRun Animal with Run
class $AnimalRunFly extends $AnimalRun with Fly
class $AnimalRunFlyFlyEnhance extends $AnimalRunFly with FlyEnhance
class Bird extends $AnimalRunFlyFlyEnhance
所以上面的代码会额外创建三个类, 由于 mixin
优先级高于 extends
, 所以:
// 使用 Fly 里的 fly 方法
class $AnimalRunFly extends $AnimalRun with Fly
// 使用 FlyEnhance 里的 fly 方法
class $AnimalRunFlyFlyEnhance extends $AnimalRunFly with FlyEnhance
这就是 Dart 为什么最终会选择使用 FlyEnhance
而不是 Fly
里的 fly()
方法
如果一个类使用 mixin 特性, 那么这个类的实例也是 mixin 类的子类型:
print(bird is Animal);
print(bird is Run);
print(bird is Fly);
print(bird is FlyEnhance);
//true
如果一个类只用于被 mixin
, 可以在声明类的时候使用 mixin
关键字代替 class
关键字
这个类则不能被继承(extends), 但可以被实现(implements)
还可以通过 on
关键字来指定 mixin
类的需要的父类
class SuperA{
}
mixin A on SuperA{
}
也就是说类 A
, 对 SuperA
有依赖, 那么其他类在 with
类 A
的时候, 必须继承或者实现 SuperA
类
class B extends SuperA with A{
}
从中我们可以看出 mixin
机制对复用代码提供了非常大的便利和灵活性
关于 Dart 面向对象就先分析到这里
Reference
- https://dart.dev/guides/language/language-tour
- https://stackoverflow.com/questions/12649573/how-do-you-build-a-singleton-in-dart
- https://stackoverflow.com/questions/45901297/when-to-use-mixins-and-when-to-use-interfaces-in-dart/45903671#45903671
联系我
所有关于 Retrofit 的使用案例都在我的 AndroidAll GitHub 仓库中。该仓库除了 Retrofit,还有其他 Android 其他常用的开源库源码分析,如「RxJava」「Glide」「LeakCanary」「Dagger2」「Retrofit」「OkHttp」「ButterKnife」「Router」等。除此之外,还有完整的 Android 程序员所需要的技术栈思维导图,欢迎享用。
下面是我的公众号,干货文章不错过,有需要的可以关注下,有任何问题可以联系我: