泛型
如果您查看基本數(shù)組類(lèi)型List的API文檔,您將看到該類(lèi)型實(shí)際上是List<E> <..>標(biāo)記列表為具有正式類(lèi)型參數(shù)的泛型(或參數(shù)化)類(lèi)型a。按照慣例,類(lèi)型變量有單字母名稱(chēng),例如E、T、S、K和V。
E, T, K, V是泛型中常用的幾個(gè)名稱(chēng),實(shí)際上定義泛型時(shí)完全可以不使用它們.
不過(guò)這幾個(gè)字母用得人多了,也就有了可讀性上的意義,這樣可以更好的進(jìn)行協(xié)作.
E代表Element,元素.
T代表Type,類(lèi)型.
K代表Key,鍵.
V代表Value,值.
為什么使用泛型?
泛型通常是為了類(lèi)型安全而必需的,但是它們比僅僅允許您的代碼運(yùn)行有更多的好處:
- 適當(dāng)?shù)刂付ǚ盒皖?lèi)型會(huì)生成更好的代碼。
- 您可以使用泛型來(lái)減少代碼重復(fù)。
如果想要List只包含字符串,可以將其聲明為list <String>(讀作“String列表”)。通過(guò)這種方式,您的程序員伙伴和您的工具可以檢測(cè)到向列表分配非字符串可能是一個(gè)錯(cuò)誤。這里有一個(gè)例子:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
使用泛型的另一個(gè)原因是為了減少代碼重復(fù)。泛型允許您在許多類(lèi)型之間共享一個(gè)接口和實(shí)現(xiàn),同時(shí)還可以利用靜態(tài)分析。例如,假設(shè)您創(chuàng)建了一個(gè)緩存對(duì)象的接口:
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
您發(fā)現(xiàn)需要此接口的特定字符串版本,因此創(chuàng)建了另一個(gè)接口:
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
稍后,您決定要此接口的特定于數(shù)字的版本……您就知道了。
泛型類(lèi)型可以省去創(chuàng)建所有這些接口的麻煩。相反,您可以創(chuàng)建一個(gè)帶有類(lèi)型參數(shù)的接口:
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
在這段代碼中,T是替代類(lèi)型。它是一個(gè)占位符,您可以將其視為開(kāi)發(fā)人員稍后將定義的類(lèi)型。
使用集合文字
List和Map文字可以參數(shù)化。參數(shù)化的字面值就像您已經(jīng)看到的字面值一樣,只是在左括號(hào)前面添加了<type>(用于列表)或<keyType, valueType>(用于映射)。下面是使用類(lèi)型化文字的例子:
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'
};
使用帶構(gòu)造函數(shù)的參數(shù)化類(lèi)型
若要在使用構(gòu)造函數(shù)時(shí)指定一個(gè)或多個(gè)類(lèi)型,請(qǐng)將類(lèi)型放在尖括號(hào)(<…>)中,剛好在類(lèi)名之后。例如:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
var nameSet = Set<String>.from(names);
下面的代碼創(chuàng)建了一個(gè)具有整型鍵和類(lèi)型視圖值的映射:
var views = Map<int, View>();
泛型集合及其包含的類(lèi)型
Dart泛型類(lèi)型被具體化了,這意味著它們?cè)谶\(yùn)行時(shí)攜帶著自己的類(lèi)型信息。例如,您可以測(cè)試集合的類(lèi)型:
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
注意:相反,Java中的泛型使用擦除,這意味著泛型類(lèi)型參數(shù)在運(yùn)行時(shí)被刪除。在Java中,可以測(cè)試對(duì)象是否為列表,但不能檢查它是否為列表<String>。
限制參數(shù)化類(lèi)型
在實(shí)現(xiàn)泛型類(lèi)型時(shí),您可能希望限制其參數(shù)的類(lèi)型。您可以使用extend來(lái)實(shí)現(xiàn)這一點(diǎn)。
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
可以使用SomeBaseClass或它的任何子類(lèi)作為通用參數(shù):
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
也可以不指定通用參數(shù):
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
指定任何非SomeBaseClass類(lèi)型將導(dǎo)致錯(cuò)誤:
var foo = Foo<Object>();
使用泛型方法
最初,Dart的通用支持僅限于類(lèi)。一種較新的語(yǔ)法,稱(chēng)為泛型方法,允許對(duì)方法和函數(shù)進(jìn)行類(lèi)型參數(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;
}
這里的第一個(gè)泛型類(lèi)型參數(shù)(<T>)允許您在幾個(gè)地方使用類(lèi)型參數(shù)T:
- 在函數(shù)的返回類(lèi)型(T)中。
- 在參數(shù)的類(lèi)型中(列表<T>)。
- 在局部變量(T tmp)的類(lèi)型中。