做者 | 弗拉德
來源 | 弗拉德(公衆號:fulade_me)html
若是你查看數組的API文檔,你會發現數組List
的實際類型爲List<E>
。<>
符號表示數組是一個泛型(或參數化類型)一般使用一個字母來表明類型參數,好比E、T、S、K 和 V 等等。數組
泛型經常使用於須要要求類型安全的狀況,可是它對代碼運行也有好處:緩存
好比你想聲明一個只能包含String
類型的數組,你能夠將該數組聲明爲List<String>
,這表示只能包含字符串類型的數組。這樣的話就能夠很容易避免由於在該數組放入非String
類變量而致使的諸多問題,同時編譯器以及其餘閱讀代碼的人均可以很容易地發現並定位問題:安全
var names = List<String>(); names.addAll(['Seth', 'Kathy', 'Lars']); names.add(42); // 這樣寫就會報錯
另外一個使用泛型的緣由是能夠減小重複代碼。泛型可讓你在多個不一樣類型實現之間共享同一個接口聲明,好比下面的例子中聲明瞭一個類用於緩存對象的接口:ide
/// 定義一個 抽象類 abstract class ObjectCache { Object getByKey(String key); void setByKey(String key, Object value); }
不久後你可能又會想專門爲String
類對象作一個緩存,因而又有了專門爲String
作緩存的類:函數
/// 另一個抽象類 abstract class StringCache { String getByKey(String key); void setByKey(String key, String value); }
若是過段時間你又想爲數字類型也建立一個類,那麼就會有不少諸如此類的代碼。
這時候能夠考慮使用泛型來聲明一個類,讓不一樣類型的緩存實現該類作出不一樣的具體實現便可:code
abstract class Cache<T> { T getByKey(String key); void setByKey(String key, T value); }
在上述代碼中,T
是一個替代類型。其至關於類型佔位符,在開發者調用該接口的時候會指定具體類型。htm
List、Set
以及Map
字面量也能夠是參數化的。定義參數化的List
只需在中括號前添加<type>
;定義參數化的Map
只須要在大括號前添加 <keyType, valueType>
:對象
var names = <String>['小芸', '小芳', '小民']; var uniqueNames = <String>{'小芸', '小芳', '小民'}; var pages = <String, String>{ 'index.html': '主頁', 'robots.txt': '網頁機器人提示', 'humans.txt': '咱們是人類,不是機器' };
在調用構造方法時也能夠使用泛型,只需在類名後用尖括號<...>
將一個或多個類型包裹便可:接口
var nameSet = Set<String>.from(names);
下面代碼建立了一個鍵爲Int
類型,值爲View
類型的Map
對象:
var views = Map<int, View>();
Dart的泛型類型是固化的,這意味着即使在運行時也會保持類型信息:
var names = List<String>(); names.addAll(['小芸', '小芳', '小民']); print(names is List<String>); // true
有時使用泛型的時候可能會想限制泛型的類型範圍,這時候能夠使用extends
關鍵字:
class Foo<T extends SomeBaseClass> { // 具體實現…… String toString() => "'Foo<$T>' 的實例"; } class Extender extends SomeBaseClass {...}
這時候就能夠使用SomeBaseClass
或者它的子類來做爲泛型參數:
var someBaseClassFoo = Foo<SomeBaseClass>(); var extenderFoo = Foo<Extender>();
這時候也能夠指定無參數的泛型,這時無參數泛型的類型則爲 Foo<SomeBaseClass>
:
var foo = Foo(); print(foo); // 'Foo<SomeBaseClass>' 的例
將非SomeBaseClass
的類型做爲泛型參數則會致使編譯錯誤:
/// 這樣寫是會報錯的 var foo = Foo<Object>();
起初Dart
只支持在類的聲明時指定泛型,如今一樣也能夠在方法上使用泛型,稱之爲泛型方法
:
T first<T>(List<T> ts) { // 處理一些初始化工做或錯誤檢測…… T tmp = ts[0]; // 處理一些額外的檢查…… return tmp; }
方法 first<T>
的泛型T
能夠在以下地方使用:
T
。List<T>
。T tmp
。