天天看點

Dart —— 泛型

Dart彙總請點選這裡

泛型

泛型是任何強類型語言都擁有的一種機制,用<>來标注類型。

使用泛型有兩個優點:

  • 必須指定資料的類型,類型安全,更易解讀;
  • 避免代碼重複,使代碼更簡潔;

在 API 文檔中你會發現基礎數組類型 List 的實際類型是 List 。 <…> 符号将 List 标記為 泛型 (或 參數化) 類型。 這種類型具有形式化的參數。 通常情況下,使用一個字母來代表類型參數, 例如 E, T, S, K, 和 V 等。

為什麼使用泛型

在類型安全上通常需要泛型支援, 它的好處不僅僅是保證代碼的正常運作:

正确指定泛型類型可以提高代碼品質。

使用泛型可以減少重複的代碼。

如果想讓

List

僅僅支援字元串類型, 可以将其聲明為

List<String>

(讀作“字元串類型的 list ”)。 那麼,當一個非字元串被指派給了這個 list 時,開發工具就能夠檢測到這樣的做法可能存在錯誤。 例如:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // 錯誤
           

另外一個使用泛型的原因是減少重複的代碼。 泛型可以在多種類型之間定義同一個實作, 同時還可以繼續使用檢查模式和靜态分析工具提供的代碼分析功能。 例如,假設你建立了一個用于緩存對象的接口:

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}
           

後來發現需要一個相同功能的字元串類型接口,是以又建立了另一個接口:

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
           

後來,又發現需要一個相同功能的數字類型接口 … 這裡你應該明白了。

泛型可以省去建立所有這些接口的麻煩。 通過建立一個帶有泛型參數的接口,來代替上述接口:

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
           

在上面的代碼中,T 是一個備用類型。 這是一個類型占位符,在開發者調用該接口的時候會指定具體類型。

使用集合字面量

List , Set 和 Map 字面量也是可以參數化的。 參數化字面量和之前的字面量定義類似, 對于 List 或 Set 隻需要在聲明語句前加

<type>

字首, 對于 Map 隻需要在聲明語句前加

<keyType, valueType>

字首, 下面是參數化字面量的示例:

var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
  'index.html': 'Homepage',
  'robots.txt': 'Hints for web robots',
  'humans.txt': 'We are people, not machines'
};
           

使用泛型類型的構造函數

在調用構造函數的時,在類名字後面使用尖括号(<…>)來指定泛型類型。 例如:

下面代碼建立了一個 key 為 integer, value 為 View 的 map 對象:

運作時中的泛型集合

Dart 中泛型類型是 固化的,也就是說它們在運作時是攜帶着類型資訊的。 例如, 在運作時檢測集合的類型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
           
提示: 相反,Java中的泛型會被 擦除 ,也就是說在運作時泛型類型參數的資訊是不存在的。 在Java中,可以測試對象是否為

List

類型, 但無法測試它是否為

List<String>

限制泛型類型

使用泛型類型的時候, 可以使用 extends 實作參數類型的限制。

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T>'";
}

class Extender extends SomeBaseClass {...}
           

可以使用 SomeBaseClass 或其任意子類作為通用參數:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
           

也可以不指定泛型參數:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
           

指定任何非 SomeBaseClass 類型會導緻錯誤:

使用泛型函數

最初,Dart 的泛型隻能用于類。 新文法_泛型方法_,允許在方法和函數上使用類型參數:

T first<T>(List<T> ts) {
  // Do some initial work or error checking, then...
  T tmp = ts[0];
  // Do some additional checking or processing...
  return tmp;
}
           

這裡的

first (<T>)

泛型可以在如下地方使用參數

T

函數的傳回值類型 (

T

).

參數的類型 (

List<T>

).

局部變量的類型 (

T tmp

).

關于泛型的更多資訊,參考 使用泛型函數