(五)Flutter学习之Dart面向对象

2,125 阅读8分钟

前言

Dart 是一门面向对象的语言, 所有的类都是继承自 Object , 除了支持传统的 继承、封装、多态, 还有基于组合(Mixin-based)的继承特性, 这个特性也是本文将会重点介绍的地方

既然是面向对象的语言, 先让我们来看下如何定义一个类:

class ClassName{
}

通过 class 关键字定义类, 语法和其他主流的语言没有什么差别

有过使用面向对象语言经验的开发者知道, 一般类由成员变量, 成员方法, 构造方法, 静态变量, 静态方法组成

成员变量和静态变量

下面我们来看下如何声明成员变量:

class Point {
  // 声明成员变量 x
  num x;
  // 声明成员变量 y
  num y;
}

成员变量默认会生成一个隐式的 getter 方法, 并且 非final 的成员变量也会隐式的生成 setter 方法

除此以外, 开发者还可以使用 getset 关键字实现 gettersetter 方法来创建新的属性, 如:

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<String, num> 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 中这是允许的, 在 Javaimplements 关键后面只能放接口

如果 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 有依赖, 那么其他类在 withA 的时候, 必须继承或者实现 SuperA

class B extends SuperA with A{ 
}

从中我们可以看出 mixin 机制对复用代码提供了非常大的便利和灵活性

关于 Dart 面向对象就先分析到这里

Reference

dart.dev/guides/lang…

stackoverflow.com/questions/1…

stackoverflow.com/questions/4…


联系我

下面是我的公众号,干货文章不错过,有需要的可以关注下,有任何问题可以联系我:

公众号:  chiclaim