小菜在 Android 開發過程中,會通過重載實現根據不同的參數類型生成類似方法,進一步可以通過 泛型 進一步提取基類方法;而對於 Dart 而言,爲了解決多種方式構造對象的場景,也可以通過 泛型 來提取基類;今天小菜簡單學習一下 Dart 中的 Generics 泛型;
// Android
public static void getValue(boolean value) {}
public static void getValue(int value) {}
public static void getValue(long value) {}
public static void getValue(String value) {}
Generics
Generics 泛型是用於解決 類/接口/方法 複用性以及對不特定類型進行數據校驗的一種方式;通常用 <...> 符號表示,其中參數一般用 T、E、S、K、V 字母代表;
泛型優勢
小菜爲實現上述 Java 對應傳遞固定類型參數返回固定類型方法,因 Dart 不支持重載,可以設置多個不同名稱的方法或不同的命名構造函數;
bool getBoolValue(bool value) {}
bool saveIntValue(int value) {}
String saveStringValue(String value) {}
由此可見,該方式需要設置多個類似的方法,代碼過於冗餘;且 Dart 中的類型實際是可選的,即在 Dart 中函數類型,可以省略參數類型和變量類型等;因此小菜嘗試不指定傳參類型和返回類型,雖然可以避免代碼冗餘,但是卻放棄了代碼檢查;
getValue(value) => value;
此時,我們可以考慮用 泛型 方式來處理,而泛型的優勢就是適當地指定泛型可以更好地幫助代碼生成和減少代碼重複避免代碼冗餘;
T getValue<T>(T value) => value;
print('SpUtils -> getValue(bool) -> ${getValue(true)} -> ${getValue<bool>(true)}');
print('SpUtils -> getValue(int) -> ${getValue(123)} -> ${getValue<int>(123)}');
print('SpUtils -> getValue(String) -> ${getValue('阿策小和尚')} -> ${getValue<String>('阿策小和尚')}');
泛型方法
上述方式中,小菜便是定義了一個 getValue 的泛型方法,但是泛型的應用比較靈活,可以只限制參數或返回類型或兩者均限制;
1. 函數參數爲泛型類型
getValue() 可以當作一個普通的函數使用,但是爲了限制參數類型校驗,可以在參數前加入固定類型;因爲限制了 getValue<String> 因此參數只能傳遞 String 類型,若傳入其他類型參數則會異常提示;
getValue<T>(T value) => value;
print('SpUtils -> getValue(String) -> ${getValue<String>('阿策小和尚')}');
// 異常參數類型 getValue<String>(123)
// The argument type int can’t be assigned to the parameter type String.
2. 函數返回值爲泛型類型
getValue() 前添加泛型限制時,即限制了返回參數爲泛型類型,其中的返回內容不能限制爲固定的某一種類型,此時參數和返回值均會進行不確定類型校驗;
T getValue<T>(T value) => value;
print('SpUtils -> getValue(String) -> ${getValue<String>('阿策小和尚')}');
泛型類
Dart 中常用的 List 或 Set 等基礎數組類型均爲泛型類;小菜以 List 爲例,創建了一個 MyList 的泛型類;
class MyList<T> {
List _list = List<T>();
void add<T>(T value) {
this._list.add(value);
}
get myList => this._list;
}
小菜不限制類型,可以在 MyList 中添加任意類型的數據;當限制傳入數據爲 int 或 String 類型時,則只能傳入固定類型數據,否則會異常提示;即通過泛型對不確定類型進行了數據校驗;
MyList myList1 = MyList();
myList1.add(true);
myList1.add(123);
myList1.add('阿策小和尚');
print('MyList -> ${myList1.myList}');
/// I/flutter (13273): MyList -> [true, 123, 阿策小和尚]
MyList myList2 = MyList<int>();
myList2.add(true); // 只能傳入固定 int 類型
myList2.add(123);
print('MyList -> ${myList2.myList}');
/// type 'bool' is not a subtype of type 'int' of 'value'
泛型接口
Dart 中定義泛型接口和泛型類是一樣的,Dart 中定義接口方式可以是普通類也可以是抽象類;小菜定義了一個 SP<T> 接口,添加了 get / set 方法;
abstract class SP<T> {
void set(String key, T value);
T get(String key);
}
class SpUtils<T> implements SP<T> {
Map map = new Map();
@override
T get(String key) {
return map[key];
}
@override
void set(String key, T value) {
map[key] = value;
}
}
使用時與泛型類一致,在不限制 set 類型時,可以是任意數據類型,而若設置 SpUtils<String> 時,則限制 set 內容只能爲 String 類型,若傳入其他類型則會異常提示;
SpUtils spUtils = SpUtils();
spUtils.set('name', '阿策小和尚');
spUtils.set('age', 18);
print('SpUtils -> ${spUtils.get('name')} -> ${spUtils.get('age')}');
/// I/flutter (13273): SpUtils -> 阿策小和尚 -> 18
SpUtils spUtils2 = SpUtils<String>();
spUtils2.set('name', '阿策小和尚');
spUtils2.set('age', 18); // 只能傳入固定 String 類型
print('SpUtils -> ${spUtils2.get('name')} -> ${spUtils2.get('age')}');
/// type 'int' is not a subtype of type 'String' of 'value'
泛型約束
在使用泛型類型時可以限制其參數類型,例如,可以使用 extends 在進行限制;通過 extends 可以限制其當前參數類型及其子類參數類型;
class Animal {}
class FlyAnimal extends Animal {}
class LandAnimal extends Animal {}
class Bird extends FlyAnimal {}
class Dog extends LandAnimal {}
class Cat extends LandAnimal {}
class Foo<T extends LandAnimal> {}
Foo foo1 = Foo<LandAnimal>();
Foo foo2 = Foo<Cat>();
Foo foo3 = Foo<Dog>();
Foo foo4 = Foo<FlyAnimal>(); /// 異常類型;
小菜對 泛型 理解僅限於日常應用,對於 協變 / 逆變 等理解還不到位;如有錯誤,請多多指導!
來源: 阿策小和尚