Dart中的类(下)

1,340 阅读6分钟

Methods

方法是为object提供行为的函数。

实例方法

对象的实例方法可以访问实例变量和this。以下示例中的distanceTo()方法是实例方法的例子:

import 'dart:math';

class Point {
  num x, y;

  Point(this.x, this.y);
	// 这个方法中distanceTo可以访问this.x和this.y
  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
Point point = Point(4, 6);
Point anthorPoint = Point(1, 2);
print(point.distanceTo(anthorPoint)); // 5

Getters and setters

gettersetter是提供对象属性的读写访问权限的特殊方法。回想一下,其实每个实例变量都有一个隐式getter,如果设置恰当的话还有一个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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  // 当给.right赋值时,会执行set right对应的方法
  rect.right = 12; // left = 12 - 20
  assert(rect.left == -8);
}

使用gettersetter,可以从实例变量开始,稍后使用方法包装它们,而无需更改代码。
无论是否明确定义了getter,增量(++)等运算符都以预期的方式工作。为避免任何意外的副作用,++操作符只会调用一次getter,将其值保存在临时变量中。

抽象方法

实例、gettersetter方法可以是抽象的,定义一个接口,但将其实现留给其他类。抽象方法只能存在于抽象类(abstract classes)中:
要使方法成为abstract,请使用分号(;)结尾而不是方法体:

abstract class Doer {
  void doSomething(); // 定义抽象方法,只能在抽象类中定义,具体实现留给其他类
}

class EffectiveDoer extends Doer {
  void doSomething() {
    方法实现,这不是一个抽象方法。
  }
}

抽象类

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

// 这个类被定义为抽象类无法直接实例化,可以提供工厂构造函数来实例化
abstract class AbstractContainer {
  void updateChildren(); // 抽象方法
}

隐式接口

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

// A person 包含隐式的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.';
}

// person接口的实现.
class Impostor implements Person {
  get _name => '';
	// 自己实现greet方法
  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'))); // Hello, Bob. I am Kathy.
  print(greetBob(Impostor())); // Hi Bob. Do you know who I am?
}

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

class Point implements Comparable, Location {...}

继承一个类

使用extends创建子类,使用super来引用超类:

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}
// 继承Television类
class SmartTelevision extends Television {
  void turnOn() {
    // super调用超类方法。
    super.turnOn();
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}

重写成员

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

// 前面的SmartTelevision类中就重写了Television中的turnOn方法
// @override只是一个标识符,没有实际意义。
class SmartTelevision extends Television {
  @override
  void turnOn() {...}
  // ···
}

覆盖操作符

之前介绍操作符是已经说过,操作符是可以覆盖的。例如,如果定义Vector类,则可以定义一个+方法来添加两个vectors

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)); // v + w 将执行 (Vector v) => Vector(x + v.x, y + v.y)
  assert(v - w == Vector(0, 1)); // v - w 将执行 (Vector v) => Vector(x - v.x, y - v.y)
}

如果覆盖==,则还应该覆盖ObjecthashCode getter。有关重写==hashCode的示例,请参阅Implementing map keys

noSuchMethod():

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

class A {
  // 除非你覆盖了noSuchMethod方法,否则会在使用不存在的方法时报错
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

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

  1. 接收者具有静态类型dynamic
  2. 接收者有一个定义未实现方法的静态类型(比如abstract),接收器的动态类型有一个noSuchMethod()的实现,它与类Object中的实现不同.

示例1:

main() {
  dynamic person = new Person(); // person 声明为dynamic,满足了第一个条件
  print(person.missing('shubham'));  // 调用了不存在的方法missing
  // 打印结果: Got the Symbol("missing") with arguments [shubham]
}


class Person {
    @override  //overring noSuchMethod
    noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}

示例2:

class Person {
  // 定义了一个没有实现方法体的方法,前面说过抽象方法只能存在抽象类中,但是这里定义了noSuchMethod,所以可以存在
  missing(int age,String name);
	
  @override //overriding noSuchMethod
  noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}

main() {
  dynamic person = new Person(); // person 声明为dynamic
  print(person.missing('shubham')); // calling abstract method
  // 打印结果: Got the Symbol("missing") with arguments [shubham]
}

枚举类型

枚举类型是一种特殊类,用于表示固定数量的常量值。

使用枚举类

使用enum关键字声明枚举类型:

enum Color { red, green, blue }

枚举中的每个值都有一个索引getter,它返回枚举声明中值的从零开始的位置。例如,第一个值具有索引0,第二个值具有索引1:

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

要获取枚举中所有值的List,请使用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: // 没有这句话,会收到一条警告
    print(aColor); // 'Color.blue'
}

枚举类型具有以下限制:

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

mixins

Mixins是一种在多个类层次结构中重用类代码的方法(由于dart是单继承类,所以对于想复用多个类,请使用mixins)。
维基百科中定义:

在面向对象的语言中,mixins类是一个可以把自己的方法提供给其他类使用,但却不需要成为其他类的父类。

要使用mixin,请使用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,请创建一个继承自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,使用on来指定所需的超类:

mixin MusicalPerformer on Musician {
  // ···
}

类变量和方法

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

Static variables

静态变量(类变量)对于类范围的状态和常量很有用:

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

void main() {
  // 直接在类上调用静态变量,而不需要实例化
  assert(Queue.initialCapacity == 16);
}

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

Static methods

静态方法(类方法)不对实例进行操作,因此无法访问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);
  print(distance); // 2.8284271247461903
}

对于常用或广泛使用的实用工具类和功能,请考虑使用顶级函数而不是静态方法。
你可以使用静态方法作为编译时常量。例如,可以将静态方法作为参数传递给常量构造函数。