類(下)

2020-02-03 23:34 更新

方法

方法是為對(duì)象提供行為的函數(shù)。

實(shí)例方法

對(duì)象的實(shí)例方法可以訪問 this 和實(shí)例變量。 以下示例中的 distanceTo() 方法就是實(shí)例方法:

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);
  }
}

Getter 和 Setter

Getter 和 Setter 是用于對(duì)象屬性讀和寫的特殊方法。 回想之前的例子,每個(gè)實(shí)例變量都有一個(gè)隱式 Getter ,通常情況下還會(huì)有一個(gè) Setter 。 使用 get 和 set 關(guān)鍵字實(shí)現(xiàn) Getter 和 Setter ,能夠?yàn)閷?shí)例創(chuàng)建額外的屬性。

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

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

  // 定義兩個(gè)計(jì)算屬性: 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);
}

最開始實(shí)現(xiàn) Getter 和 Setter 也許是直接返回成員變量; 隨著需求變化, Getter 和 Setter 可能需要進(jìn)行計(jì)算處理而使用方法來(lái)實(shí)現(xiàn); 但是,調(diào)用對(duì)象的代碼不需要做任何的修改。

提示: 類似 (++) 之類操作符不管是否定義了 getter 方法,都能夠正確的執(zhí)行。 為了避免一些問題,操作符只調(diào)用一次 getter 方法, 然后把值保存到一個(gè)臨時(shí)的變量中。

抽象方法

實(shí)例方法, getter, 和 setter 方法可以是抽象的, 只定義接口不進(jìn)行實(shí)現(xiàn),而是留給其他類去實(shí)現(xiàn)。 抽象方法只存在于 抽象類 中。

定義一個(gè)抽象函數(shù),使用分號(hào) (;) 來(lái)代替函數(shù)體:

abstract class Doer {
  // 定義實(shí)例變量和方法 ...

  void doSomething(); // 定義一個(gè)抽象方法。
}

class EffectiveDoer extends Doer {
  void doSomething() {
    // 提供方法實(shí)現(xiàn),所以這里的方法就不是抽象方法了...
  }
}

調(diào)用抽象方法會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤。


抽象類

使用 abstract 修飾符來(lái)定義 抽象類 — 抽象類不能實(shí)例化。 抽象類通常用來(lái)定義接口,以及部分實(shí)現(xiàn)。 如果希望抽象類能夠被實(shí)例化,那么可以通過定義一個(gè) 工廠構(gòu)造函數(shù) 來(lái)實(shí)現(xiàn)。

抽象類通常具有 抽象方法。 下面是一個(gè)聲明具有抽象方法的抽象類示例:

// 這個(gè)類被定義為抽象類,
// 所以不能被實(shí)例化。
abstract class AbstractContainer {
  // 定義構(gòu)造行數(shù),字段,方法...

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


隱式接口

每個(gè)類都隱式的定義了一個(gè)接口,接口包含了該類所有的實(shí)例成員及其實(shí)現(xiàn)的接口。 如果要?jiǎng)?chuàng)建一個(gè) A 類,A 要支持 B 類的 API ,但是不需要繼承 B 的實(shí)現(xiàn), 那么可以通過 A 實(shí)現(xiàn) B 的接口。

一個(gè)類可以通過 implements 關(guān)鍵字來(lái)實(shí)現(xiàn)一個(gè)或者多個(gè)接口, 并實(shí)現(xiàn)每個(gè)接口要求的 API。 例如:

// person 類。 隱式接口里面包含了 greet() 方法聲明。
class Person {
  // 包含在接口里,但只在當(dāng)前庫(kù)中可見。
  final _name;

  // 不包含在接口里,因?yàn)檫@是一個(gè)構(gòu)造函數(shù)。
  Person(this._name);

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

// person 接口的實(shí)現(xiàn)。
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()));
}

下面示例演示一個(gè)類如何實(shí)現(xiàn)多個(gè)接口: Here’s an example of specifying that a class implements multiple interfaces:

class Point implements Comparable, Location {...}


擴(kuò)展類(繼承)

使用 extends 關(guān)鍵字來(lái)創(chuàng)建子類, 使用 super 關(guān)鍵字來(lái)引用父類:

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

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

重寫類成員

子類可以重寫實(shí)例方法,getter 和 setter。 可以使用 @override 注解指出想要重寫的成員:

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

To narrow the type of a method parameter or instance variable in code that is type safe, you can use the covariant keyword.

重寫運(yùn)算符

下標(biāo)的運(yùn)算符可以被重寫。 例如,想要實(shí)現(xiàn)兩個(gè)向量對(duì)象相加,可以重寫 + 方法。

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

提示: 你可能會(huì)被提示 != 運(yùn)算符為非可重載運(yùn)算符。 因?yàn)?nbsp;e1 != e2 表達(dá)式僅僅是 !(e1 == e2) 的語(yǔ)法糖。

下面示例演示一個(gè)類重寫 + 和 - 操作符:

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);

  // 運(yùn)算符 == 和 hashCode 部分沒有列出。 有關(guān)詳情,請(qǐng)參考下面的注釋。
  // ···
}

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

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

如果要重寫 == 操作符,需要重寫對(duì)象的 hashCode getter 方法。 重寫 == 和 hashCode 的實(shí)例,參考 Implementing map keys.

有關(guān)重寫的更多介紹,請(qǐng)參考 擴(kuò)展類(繼承).

noSuchMethod()

當(dāng)代碼嘗試使用不存在的方法或?qū)嵗兞繒r(shí), 通過重寫 noSuchMethod() 方法,來(lái)實(shí)現(xiàn)檢測(cè)和應(yīng)對(duì)處理:

class A {
  // 如果不重寫 noSuchMethod,訪問
  // 不存在的實(shí)例變量時(shí)會(huì)導(dǎo)致 NoSuchMethodError 錯(cuò)誤。
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

除非符合下面的任意一項(xiàng)條件, 否則沒有實(shí)現(xiàn)的方法不能夠被調(diào)用:

  • receiver 具有 dynamic 的靜態(tài)類型 。
  • receiver 具有靜態(tài)類型,用于定義為實(shí)現(xiàn)的方法 (可以是抽象的), 并且 receiver 的動(dòng)態(tài)類型具有 noSuchMethod() 的實(shí)現(xiàn), 該實(shí)現(xiàn)與 Object 類中的實(shí)現(xiàn)不同。

有關(guān)更多信息,參考 noSuchMethod forwarding specification.


枚舉類型

枚舉類型也稱為 enumerations 或 enums , 是一種特殊的類,用于表示數(shù)量固定的常量值。

使用枚舉

使用 enum 關(guān)鍵字定義一個(gè)枚舉類型:

enum Color { red, green, blue }

枚舉中的每個(gè)值都有一個(gè) index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)。 例如,第一個(gè)枚舉值的索引是 0 , 第二個(gè)枚舉值的索引是 1。

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

使用枚舉的 values 常量, 獲取所有枚舉值列表( list )。

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

可以在 switch 語(yǔ)句 中使用枚舉, 如果不處理所有枚舉值,會(huì)收到警告:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 沒有這個(gè),會(huì)看到一個(gè)警告。
    print(aColor); // 'Color.blue'
}

枚舉類型具有以下限制:

  • 枚舉不能被子類化,混合或?qū)崿F(xiàn)。
  • 枚舉不能被顯式實(shí)例化。

有關(guān)更多信息,參考 Dart language specification 。


為類添加功能: Mixin

Mixin 是復(fù)用類代碼的一種途徑, 復(fù)用的類可以在不同層級(jí),之間可以不存在繼承關(guān)系。

通過 with 后面跟一個(gè)或多個(gè)混入的名稱,來(lái) 使用 Mixin , 下面的示例演示了兩個(gè)使用 Mixin 的類:

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

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

通過創(chuàng)建一個(gè)繼承自 Object 且沒有構(gòu)造函數(shù)的類,來(lái) 實(shí)現(xiàn) 一個(gè) Mixin 。 如果 Mixin 不希望作為常規(guī)類被使用,使用關(guān)鍵字 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 可以調(diào)用 Mixin 自身沒有定義的方法 - 使用 on 來(lái)指定可以使用 Mixin 的父類類型:

mixin MusicalPerformer on Musician {
  // ···
}

版本提示: mixin 關(guān)鍵字在 Dart 2.1 中被引用支持。 早期版本中的代碼通常使用 abstract class 代替。 更多有關(guān) Mixin 在 2.1 中的變更信息,請(qǐng)參見 Dart SDK changelog 和 2.1 mixin specification 。

提示: 對(duì) Mixin 的一些限制正在被移除。 關(guān)于更多詳情,參考 proposed mixin specification.

有關(guān) Dart 中 Mixin 的理論演變,參考 A Brief History of Mixins in Dart.


類變量和方法

使用 static 關(guān)鍵字實(shí)現(xiàn)類范圍的變量和方法。

靜態(tài)變量

靜態(tài)變量(類變量)對(duì)于類級(jí)別的狀態(tài)是非常有用的:

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

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

靜態(tài)變量只到它們被使用的時(shí)候才會(huì)初始化。

提示: 代碼準(zhǔn)守風(fēng)格推薦指南 中的命名規(guī)則, 使用 lowerCamelCase 來(lái)命名常量。

靜態(tài)方法

靜態(tài)方法(類方法)不能在實(shí)例上使用,因此它們不能訪問 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);
}

提示: 對(duì)于常見或廣泛使用的工具和函數(shù), 應(yīng)該考慮使用頂級(jí)函數(shù)而不是靜態(tài)方法。

靜態(tài)函數(shù)可以當(dāng)做編譯時(shí)常量使用。 例如,可以將靜態(tài)方法作為參數(shù)傳遞給常量構(gòu)造函數(shù)。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)