[TOC]
如果您查看基本數組類型List的API文檔,您將看到該類型實際上是List\<E>。<...>符號標記列表為泛型(或參數化)類型——具有形式類型參數的類型。根據約定,類型變量具有單字母名稱,如E、T、S、K和V。
## 為什么使用泛型
泛型通常是類型安全所必需的,他們對于寫出嚴謹高質量的代碼是很有用的:
* 適當地指定泛型類型可以生成更好的代碼。
* 您可以使用泛型來減少代碼重復。
如果您想要一個列表只包含字符串,您可以將它聲明為list (讀作“String of String”)。這樣,您和其他程序員,以及您的工具就可以檢測到將一個非字符串分配到列表中可能是一個錯誤。這里有一個例子:
~~~
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
~~~
使用泛型的另一個原因是減少代碼重復。泛型允許您在許多類型之間共享一個接口和實現,同時仍然利用靜態分析。例如,假設您創建了一個用于緩存對象的接口:
~~~
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和map字面量可以被參數化。參數化字面量和你已經認識的所有字面量一樣,僅僅是在字面量的開始括號之前添加\<type>(對于list類型來說)或者添加<keyType, valueType>(對于map類型來說)。
~~~
var names = <String>['Seth', 'Kathy', 'Lars'];
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
~~~
## 構造函數的參數化類型
要在使用構造函數時指定一個或多個類型,請將類型放在類名后面的尖括號(<…>)中。例如:
~~~
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);
~~~
下面的代碼創建了一個具有整數鍵和視圖類型值的map映射:
~~~
var views = Map<int, View>();
~~~
## 泛型集合及其包含的類型
Dart通用類型被具體化,這意味著它們在運行時攜帶它們的類型信息。例如,您可以測試集合的類型:
~~~
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
~~~
>注意:相反,Java中的泛型使用擦除,這意味著泛型類型參數在運行時被刪除。在Java中,您可以測試一個對象是否是一個列表,但不能測試它是否是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類型都會導致錯誤:
~~~
var foo = Foo<Object>();
~~~
## 使用泛型方法
最初,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)
有關泛型的更多信息,請參見[使用泛型方法]。