類(下)

2020-02-03 23:34 更新

方法

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

實例方法

對象的實例方法可以訪問 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);
  }
}

Getter 和 Setter

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

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

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

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

抽象方法

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

定義一個抽象函數(shù),使用分號 (;) 來代替函數(shù)體:

abstract class Doer {
  // 定義實例變量和方法 ...

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

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

調(diào)用抽象方法會導致運行時錯誤。


抽象類

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

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

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

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


隱式接口

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

一個類可以通過 implements 關鍵字來實現(xiàn)一個或者多個接口, 并實現(xiàn)每個接口要求的 API。 例如:

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

  // 不包含在接口里,因為這是一個構造函數(shù)。
  Person(this._name);

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

// person 接口的實現(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()));
}

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

class Point implements Comparable, Location {...}


擴展類(繼承)

使用 extends 關鍵字來創(chuàng)建子類, 使用 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() {...}
  // ···
}

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

重寫運算符

下標的運算符可以被重寫。 例如,想要實現(xiàn)兩個向量對象相加,可以重寫 + 方法。

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

提示: 你可能會被提示 != 運算符為非可重載運算符。 因為 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);

  // 運算符 == 和 hashCode 部分沒有列出。 有關詳情,請參考下面的注釋。
  // ···
}

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

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

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

有關重寫的更多介紹,請參考 擴展類(繼承).

noSuchMethod()

當代碼嘗試使用不存在的方法或?qū)嵗兞繒r, 通過重寫 noSuchMethod() 方法,來實現(xiàn)檢測和應對處理:

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

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

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

有關更多信息,參考 noSuchMethod forwarding specification.


枚舉類型

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

使用枚舉

使用 enum 關鍵字定義一個枚舉類型:

enum Color { red, green, blue }

枚舉中的每個值都有一個 index getter 方法, 該方法返回值所在枚舉類型定義中的位置(從 0 開始)。 例如,第一個枚舉值的索引是 0 , 第二個枚舉值的索引是 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 語句 中使用枚舉, 如果不處理所有枚舉值,會收到警告:

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'
}

枚舉類型具有以下限制:

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

有關更多信息,參考 Dart language specification 。


為類添加功能: Mixin

Mixin 是復用類代碼的一種途徑, 復用的類可以在不同層級,之間可以不存在繼承關系。

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

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

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

通過創(chuàng)建一個繼承自 Object 且沒有構造函數(shù)的類,來 實現(xiàn) 一個 Mixin 。 如果 Mixin 不希望作為常規(guī)類被使用,使用關鍵字 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 來指定可以使用 Mixin 的父類類型:

mixin MusicalPerformer on Musician {
  // ···
}

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

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

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


類變量和方法

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

靜態(tài)變量

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

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

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

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

提示: 代碼準守風格推薦指南 中的命名規(guī)則, 使用 lowerCamelCase 來命名常量。

靜態(tài)方法

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

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號