泛型

2020-02-03 23:38 更新

在 API 文檔中你會發(fā)現(xiàn)基礎(chǔ)數(shù)組類型 List 的實際類型是 List<E> 。 <…> 符號將 List 標(biāo)記為 泛型 (或 參數(shù)化) 類型。 這種類型具有形式化的參數(shù)。 通常情況下,使用一個字母來代表類型參數(shù), 例如 E, T, S, K, 和 V 等。


為什么使用泛型

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

  • 正確指定泛型類型可以提高代碼質(zhì)量。
  • 使用泛型可以減少重復(fù)的代碼。

如果想讓 List 僅僅支持字符串類型, 可以將其聲明為 List<String> (讀作“字符串類型的 list ”)。 那么,當(dāng)一個非字符串被賦值給了這個 list 時,開發(fā)工具就能夠檢測到這樣的做法可能存在錯誤。 例如:

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

另外一個使用泛型的原因是減少重復(fù)的代碼。 泛型可以在多種類型之間定義同一個實現(xiàn), 同時還可以繼續(xù)使用檢查模式和靜態(tài)分析工具提供的代碼分析功能。 例如,假設(shè)你創(chuàng)建了一個用于緩存對象的接口:

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

后來發(fā)現(xiàn)需要一個相同功能的字符串類型接口,因此又創(chuàng)建了另一個接口:

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

后來,又發(fā)現(xiàn)需要一個相同功能的數(shù)字類型接口 … 這里你應(yīng)該明白了。

泛型可以省去創(chuàng)建所有這些接口的麻煩。 通過創(chuàng)建一個帶有泛型參數(shù)的接口,來代替上述接口:

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

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


使用集合字面量

List , Set 和 Map 字面量也是可以參數(shù)化的。 參數(shù)化字面量和之前的字面量定義類似, 對于 List 或 Set 只需要在聲明語句前加 <type> 前綴, 對于 Map 只需要在聲明語句前加 <keyType, valueType> 前綴, 下面是參數(shù)化字面量的示例:

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


使用泛型類型的構(gòu)造函數(shù)

在調(diào)用構(gòu)造函數(shù)的時,在類名字后面使用尖括號(<...>)來指定泛型類型。 例如:

var nameSet = Set<String>.from(names);

下面代碼創(chuàng)建了一個 key 為 integer, value 為 View 的 map 對象:

var views = Map<int, View>();


運行時中的泛型集合

Dart 中泛型類型是 固化的,也就是說它們在運行時是攜帶著類型信息的。 例如, 在運行時檢測集合的類型:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true

提示: 相反,Java中的泛型會被 擦除 ,也就是說在運行時泛型類型參數(shù)的信息是不存在的。 在Java中,可以測試對象是否為 List 類型, 但無法測試它是否為 List<String> 。


限制泛型類型

使用泛型類型的時候, 可以使用 extends 實現(xiàn)參數(shù)類型的限制。

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

class Extender extends SomeBaseClass {...}

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

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

也可以不指定泛型參數(shù):

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

指定任何非 SomeBaseClass 類型會導(dǎo)致錯誤:

var foo = Foo<Object>();


使用泛型函數(shù)

最初,Dart 的泛型只能用于類。 新語法_泛型方法_,允許在方法和函數(shù)上使用類型參數(shù):

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>) 泛型可以在如下地方使用參數(shù) T :

  • 函數(shù)的返回值類型 (T).
  • 參數(shù)的類型 (List<T>).
  • 局部變量的類型 (T tmp).

關(guān)于泛型的更多信息,參考使用泛型函數(shù)

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號