Dart基础之Classes(类)下篇

444 阅读5分钟

方法(Method)

方法是为对象提供行为的函数。

实例方法

实例方法可以访问实例变量和this。下面的distanceTo()就是一个实例方法的例子:

import 'dart:math';

class Point {
  num x, y;

  Point(this.x, this.y);

  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}

Getters 和 Setters

Gettter 和 Setters 是为了能够读写对象属性的指定方法。回想一下,每个实例变量都有一个隐式getter,还有可能有一个setter。 你可以使用getset关键字通过实现getter和setter来创建其他属性:

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // 定义两个计算的属性: right 和 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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

使用getter和setter,你可以从实例变量开始,稍后使用方法包装它们,而无需更改客户端代码。

注意:无论是否明确定义了getter,增量(++)等运算符都以预期的方式工作。 为避免任何意外的副作用,运算符只需调用一次getter,将其值保存在临时变量中。

抽象方法

实例,getter和setter方法可以是抽象的,定义一个接口,但将其实现留给其他类。 抽象方法只能存在于抽象类中。(这个跟 java 里是一样的)

要使方法成为抽象,请使用分号(;)而不是方法体{}:

abstract class Doer {
  // 定义实例变量和方法...

  void doSomething(); // 定义一个抽象方法.
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供一个实现,所以这里的方法不是抽象的...
  }
}

抽象类

使用abstract修饰符定义抽象类 - 无法实例化的类。 抽象类对于定义接口非常有用,通常还有一些实现。 如果希望抽象类看起来是可实例化的,请定义工厂构造函数。

抽象类通常有抽象方法。 下面是一个声明具有抽象方法的抽象类:

// 这个类定义了 abstract 不能被实例化
abstract class AbstractContainer {
  // 定义构造函数,字段,方法...

  void updateChildren(); // 抽象方法.
}

隐式接口

每个类都隐式定义一个接口,该接口包含该类的所有实例成员及其实现的任何接口。 如果要在不继承B实现的情况下创建支持B类API的A类,则A类应实现B接口。

类通过在implements子句中声明它们然后提供接口所需的API来实现一个或多个接口。 例如:

// A person. 隐式接口包含了 greet().
class Person {
  // 在接口中, 但只有在这个库中可见
  final _name;

  // 不在接口中, 因为这是一个构造函数
  Person(this._name);

  // 在接口中
  String greet(String who) => 'Hello, $who. I am $_name.';
}

// 一个Person 接口的实现
class Impostor implements Person {
  get _name => '';

  String greet(String who) => 'Hi $who. Do you know who I am?';
}

String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));
  print(greetBob(Impostor()));
}

这是一个指定类实现多个接口的示例:

class Point implements Comparable, Location {...}

继承一个类

extends去创建一个子类,用super去指向一个超类

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

重载成员

子类可以覆盖实例方法,getter和setter。 你可以使用@override注释来覆盖成员:

class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

要在类型安全的代码中缩小方法参数或实例变量的类型,可以使用covariant关键字

可覆盖的运算符

你可以覆盖下表中展示的运算符。 例如,如果定义Vector类,则可以定义一个+方法来添加两个向量。

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
- % >>

注意:你可能已经注意到 != 不是可覆盖的运算符。 表达式e1!= e2只是!(e1==e2)的语法糖。

这是一个覆盖+-运算符的类示例:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}

如果覆盖==,则还应该覆盖Object的hashCode getter。(类似 java 中的覆写 equals(),还有 hashcode()) 有关重写==hashCode的示例,请参阅实现映射键

noSuchMethod()

要在代码尝试使用不存在的方法或实例变量时检测或做出反应,你可以覆盖noSuchMethod()

class A {
  // 除非你覆盖了 noSuchMethod,
  // 用一个不存在的成员会导致 NoSuchMethodError 结果.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

除非满足以下条件之一,否则不能调用未实现的方法:

  • 接收器具有静态类型dynamic

  • 接收器有一个定义未实现方法的静态类型(抽象是ok的),接收器的动态类型有一个noSuchMethod()实现,它与Object类中的实现不同。

有关更多信息,请参阅非正式noSuchMethod转发规范

枚举类型

枚举类型(通常称为枚举或枚举)是一种特殊类,用于表示固定数量的常量值。

使用枚举

enum申明一个枚举类型

enum Color { red, green, blue }

枚举中的每个值都有一个indexgetter,它返回枚举值的索引位置。 例如,第一个值具有索引0,第二个值具有索引1。

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);

要获取枚举中所有值的列表,用枚举.values获取枚举的常量列表。

List<Color> colors = Color.values;
assert(colors[2] == Color.blue);

你可以在switch语句中使用枚举,如果不处理所有枚举值,将收到警告:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 不写这个 default,你会看到WARNING.
    print(aColor); // 'Color.blue'
}

枚举类型具有以下限制:

  • 不能子类化,混合或实现枚举。
  • 无法显式实例化枚举。

向类添加功能:mixins(混合)

混合是一种在多个类层次结构中重用类代码的方法。

使用混合的方法是with关键字后跟一个或多个mixin命名的类。 以下示例显示了两个使用mixins的类:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

要定义一个mixin,首先创建一个mixin开头的Object扩展类,并且不能声明构造函数。 除非你希望mixin可用作常规类,否则请使用mixin关键字而不是class。 例如:

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}

要指定只有某些类型可以使用mixin - 例如,mixin可以调用它没有定义的方法 - 使用on来指定所需的超类:

mixin MusicalPerformer on Musician {
  // ···
}

版本说明:Dart 2.1中引入了对mixin关键字的支持。 早期版本中的代码通常使用抽象类。 有关2.1 mixin更改的更多信息,请参阅Dart SDK changelog和2.1 mixin规范。

类变量和方法

使用static关键字实现类范围的变量和方法。

Static 变量

静态变量(类变量)通常用作类范围的状态和常量:

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

静态变量在使用之前不会初始化。

注意:优先选择lowerCamelCase作为常量名称。

Static 方法

静态方法(类方法)不对实例进行操作,因此无法使用this关键字。 例如:

import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b);
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}

注意:对于常用或广泛使用的实用程序和功能,请考虑使用顶级(top-level)函数而不是静态方法。

你可以使用静态方法作为编译时常量。 例如,可以将静态方法作为参数传递给常量构造函数。