Android学Dart学习笔记第七节 泛型
摘要 Dart语言中的泛型使用<>符号定义,类型变量通常用单字母表示(如E、T等)。泛型的主要作用包括: 类型安全:确保集合只包含指定类型元素 代码复用:通过泛型类/方法减少重复代码 更好的静态分析:编译器能进行更精确的类型检查 集合字面量(List/Set/Map)都可以使用泛型参数。Dart的泛型在运行时保留类型信息(reified),但实际能力有限,使用时仍需谨慎。可以通过ext
序言
好的没错,代码语言之间就是如此相像,dart中也是有泛型的。在上一篇文章学习Collections时我们就用到了。这篇文章我们来系统的学习下dart中的泛型
文档
If you look at the API documentation for the basic array type, List, you’ll see that the type is actually List. The <…> notation marks List as a generic (or parameterized) type—a type that has formal type parameters. By convention, most type variables have single-letter names, such as E, T, S, K, and V.
译文:如果你看基本数组类型List的API文档,你会看到类型实际上是List。<……>表示法将List标记为泛型(或参数化)类型——具有正式类型参数的类型。按照约定,大多数类型变量的名称都是单字母的,如E、T、S、K和V。
是的,dart中也是使用<>来定义泛型,字母也是随意,虽然我们一般喜欢用T.

Why use generics?
Generics are often required for type safety, but they have more benefits than just allowing your code to run:
Properly specifying generic types results in better generated code.
You can use generics to reduce code duplication.
If you intend for a list to contain only strings, you can declare it as List (read that as “list of string”). That way you, your fellow programmers, and your tools can detect that assigning a non-string to the list is probably a mistake.
译文:为了确保类型安全,泛型通常是必需的,但除了让代码运行之外,泛型还有更多好处:
正确指定泛型类型可以生成更好的代码。
你可以使用泛型来减少代码重复。
如果你希望一个列表只包含字符串,可以将它声明为list (读作“ String的列表”)。这样,你、其他程序员和工具就能发现,将非字符串赋值给列表可能是一个错误。
看例子:
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error

这个用法大家都已经习惯成自然了,没什么稀奇的。
nother reason for using generics is to reduce code duplication. Generics let you share a single interface and implementation between many types, while still taking advantage of static analysis
译文:使用泛型的另一个原因是减少代码重复。泛型允许您在多种类型之间共享一个接口和实现,同时仍然可以利用静态分析
看下面的例子:
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);
}
这个例子大家看起来应该都不陌生,在Android中sp的管理类一般都是这么写的。
每个函数看起来都差不多,只是输入参数类型和输出结果类型不一致而已。
此时我们可以通过泛型来优化代码,减少代码量及增加了扩展性。
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
Using collection literals
在集合的值中使用泛型
List, set, and map literals can be parameterized. Parameterized literals are just like the literals you’ve already seen, except that you add (for lists and sets) or <keyType, valueType> (for maps) before the opening bracket. Here is an example of using typed literals:
译文:List、set和map字面量可以参数化。参数化字面量和你已经见过的字面量一样,不同之处是在左括号前添加(用于列表和集合)或<keyType, valueType>(用于映射)。下面是一个使用类型化字面量的例子:
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines',
};
这里也是我们昨天学习过的,顺带回顾下**[]是List,{}内是单个元素的是Set,{}内元素内部使用:分割的是Map。
Using parameterized types with constructors
在构造函数中使用泛型。
To specify one or more types when using a constructor, put the types in angle brackets (<…>) just after the class name
译文:要在使用构造函数时指定一个或多个类型,请将类型放在类名后面的尖括号中(<…>)
var nameSet = Set<String>.of(names);
The following code creates a SplayTreeMap that has integer keys and values of type View:
译文:下面的代码创建了一个SplayTreeMap,键的类型是整数,值的类型是View:
var views = SplayTreeMap<int, View>();
Generic collections and the types they contain
泛型集合及其包含的类型
Dart generic types are reified, which means that they carry their type information around at runtime.
Dart泛型类型被具体化了,这意味着它们在运行时携带着类型信息。
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
print(names is List<int>); // false
print(names is List<Object>);//true
print(names is List<dynamic>); // true
这个描述有点神奇,java是类型擦除的,所以我借助ai深入了解了下,请看下图:

在 Dart 中,你可以写 myList is List,并且它通常能正常工作,但这背后是编译器智慧和运行时的一些“包袱”在起作用,并非完整的类型具体化。它的可靠性不如 reified 场景。
总的来说就是比java强,但也有限。谨慎用吧。
Restricting the parameterized type
限制参数化类型
When implementing a generic type, you might want to limit the types that can be provided as arguments, so that the argument must be a subtype of a particular type. This restriction is called a bound. You can do this using extends.
在实现泛型类型时,你可能希望限制可作为参数提供的类型,因此参数必须是特定类型的子类型。这个限制称为边界(bound)。你可以使用extends来实现这一点。
A common use case is ensuring that a type is non-nullable by making it a subtype of Object (instead of the default, Object?).
译文:一个常见的用例是通过将一个类型设置为Object的子类型(而不是默认的Object?)来确保该类型是不可空的。
class Foo<T extends Object> {
// Any type provided to Foo for T must be non-nullable.为Foo提供的T类型必须是不可空的。
}
You can use extends with other types besides Object
译文:除了Object,还可以对其他类型使用extends
Here’s an example of extending SomeBaseClass, so that members of SomeBaseClass can be called on objects of type T.
译文:下面是一个扩展SomeBaseClass的例子,以便可以在类型T的对象上调用SomeBaseClass的成员
print(Foo<Extender>().toString()); //Instance of 'Foo<Extender>'
class SomeBaseClass {
}
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {
}
It’s OK to use SomeBaseClass or any of its subtypes as the generic argument:
译文:可以使用SomeBaseClass或它的任何子类型作为泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
It’s also OK to specify no generic argument:
译文:也可以不指定泛型参数:
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
var foo = Foo();
print(foo.toString());//Instance of 'Foo<SomeBaseClass>'
print(someBaseClassFoo.toString());//Instance of 'Foo<SomeBaseClass>'
print(extenderFoo.toString());//Instance of 'Foo<Extender>'
Specifying any non-SomeBaseClass type results in an error:
译文:指定任何非somebaseclass类型都会导致错误:

Self-referential type parameter restrictions (F-bounds)
自引用类型参数限制(F-bounds)
When using bounds to restrict parameter types, you can refer the bound back to the type parameter itself. This creates a self-referential constraint, or F-bound
译文:当使用bounds限制参数类型时,可以将绑定引用回类型参数本身。这就创建了一个自引用约束(self-referential constraint, F-bound)
int useIt = compareAndOffset(A(), A());
print(useIt);//1
int compareAndOffset<T extends Comparable<T>>(T t1, T t2) =>
t1.compareTo(t2) + 1;
abstract interface class Comparable<T> {
int compareTo(T o);
}
class A implements Comparable<A> {
int compareTo(A other) => /*...implementation...*/ 0;
}
The F-bound T extends Comparable means T must be comparable to itself. So, A can only be compared to other instances of the same type.
译文:T extends Comparable意味着T必须与自身可比较。因此,A只能与相同类型的其他实例进行比较。
Using generic methods
Methods and functions also allow type arguments
译文:方法和函数也允许类型参数
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;
}
Here the generic type parameter on first () allows you to use the type argument T in several places:
In the function’s return type (T).
In the type of an argument (List).
In the type of a local variable (T tmp).
译文:
这里first ()的泛型类型参数允许你在多个地方使用类型参数T:
函数的返回类型(T)。
参数的类型(List)。
局部变量的类型(T tmp)。
总结
在行为上,Dart 允许像 myList is List 这样的运行时类型检查,这与 Java 形成鲜明对比。然而在底层机制上,两者均基于类型擦除,Dart 的便利性源于编译器与运行时的额外支持。
更多推荐
所有评论(0)