【Dart 专题】Generics 泛型 <T>

2021-04-22 10:06:45 浏览数 (1)

和尚在 Android 开发过程中,会通过重载实现根据不同的参数类型生成类似方法,进一步可以通过 泛型 进一步提取基类方法;而对于 Dart 而言,为了解决多种方式构造对象的场景,也可以通过 泛型 来提取基类;今天和尚简单学习一下 Dart 中的 Generics 泛型;

代码语言:javascript复制
// 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 不支持重载,可以设置多个不同名称的方法或不同的命名构造函数;

代码语言:javascript复制
bool getBoolValue(bool value) {}

bool saveIntValue(int value) {}

String saveStringValue(String value) {}

由此可见,该方式需要设置多个类似的方法,代码过于冗余;且 Dart 中的类型实际是可选的,即在 Dart 中函数类型,可以省略参数类型和变量类型等;因此和尚尝试不指定传参类型和返回类型,虽然可以避免代码冗余,但是却放弃了代码检查;

代码语言:javascript复制
getValue(value) => value;

此时,我们可以考虑用 泛型 方式来处理,而泛型的优势就是适当地指定泛型可以更好地帮助代码生成和减少代码重复避免代码冗余;

代码语言:javascript复制
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 类型,若传入其他类型参数则会异常提示;

代码语言:javascript复制
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() 前添加泛型限制时,即限制了返回参数为泛型类型,其中的返回内容不能限制为固定的某一种类型,此时参数和返回值均会进行不确定类型校验;

代码语言:javascript复制
T getValue<T>(T value) => value;

print('SpUtils -> getValue(String) -> ${getValue<String>('阿策小和尚')}');

泛型类

Dart 中常用的 ListSet 等基础数组类型均为泛型类;和尚以 List 为例,创建了一个 MyList 的泛型类;

代码语言:javascript复制
class MyList<T> {
  List _list = List<T>();

  void add<T>(T value) {
    this._list.add(value);
  }

  get myList => this._list;
}

和尚不限制类型,可以在 MyList 中添加任意类型的数据;当限制传入数据为 intString 类型时,则只能传入固定类型数据,否则会异常提示;即通过泛型对不确定类型进行了数据校验;

代码语言:javascript复制
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 接口,添加了 get / set 方法;

代码语言:javascript复制
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 时,则限制 set 内容只能为 String 类型,若传入其他类型则会异常提示;

代码语言:javascript复制
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 可以限制其当前参数类型及其子类参数类型;

代码语言:javascript复制
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>();  /// 异常类型;

和尚对 泛型 理解仅限于日常应用,对于 协变 / 逆变 等理解还不到位;如有错误,请多多指导!

来源:阿策小和尚

0 人点赞