If you look at the API documentation for arrays, you’ll see that the array List is actually of type List

. <… The > symbol indicates that the array is a generic (or parameterized) type. It is common to use a single letter to represent type parameters, such as E, T, S, K, and V.

Why generics?

Generics are often used when type safety is required, but they can also be beneficial to code execution:

  • Specifying generics properly can help code generation better.

  • Using generics reduces code duplication.

For example, if you want to declare an array that can only contain strings, you can declare the array as List<String> (pronounced “List of strings”). Then, when a non-string is assigned to the List, the development tool will detect that there may be an error. Such as:

var names = List<String> (); names.addAll(['Seth'.'Kathy'.'Lars']);
names.add(42); / / error
Copy the code

Another reason to use generics is to reduce duplicate code. Generics can define the same implementation across multiple types, while continuing to use the code analysis capabilities provided by check patterns and static analysis tools. For example, suppose you create an interface for caching objects:

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}
Copy the code

It was discovered that a string-type interface with the same functionality was needed, so another interface was created:

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
Copy the code

Later, it was discovered that a digital type interface with the same functionality was needed… You get the idea here. Generics save you the trouble of creating all these interfaces. Replace the above interface by creating an interface with generic parameters:

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
Copy the code

In the above code, T is an alternate type. This is a type placeholder that specifies the type when the developer calls the interface

Use collection literals

List Set and Map literals can also be parameterized. Parameterized literals are similar to the previous literal definitions

  • For a List or Set, just prefix the statement with

    ,
  • For maps, you only need to prefix the statement with

    . Here is an example of parameterized literals:
    ,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'
};
Copy the code

Use the constructor of a generic type

When calling the constructor, use Angle brackets after the class name <… > to specify the generic type. Such as:

var nameSet = Set<String>.from(names);
Copy the code

Create a map object with key integer and value View:

var views = Map<int, View>();
Copy the code

A collection of generics at run time

Generic types in Dart are hardwired, which means they carry type information with them at run time. For example, check the type of collection at runtime:

var names = List<String> (); names.addAll(['Seth'.'Kathy'.'Lars']);
print(names is List<String>); // true
Copy the code

Tip: Instead, generics in Java are erased, meaning that information about generic type parameters is not present at run time. In Java, you can test if an object is of type List, but you cannot test if it is a List.

Restrict generic types

When using generic types, you can use extends to implement constraints on parameter types.

class Foo<T extends SomeBaseClass> {
  // Implementation goes here...
  String toString() => "Instance of 'Foo<$T> '";
}

class Extender extends SomeBaseClass {... }Copy the code

You can use SomeBaseClass or any subclass of it as a generic argument:

var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
Copy the code

You can also specify no generic arguments:

var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
Copy the code

Specifying any non-someBaseclass type results in an error:

var foo = Foo<Object> ();Copy the code

Use generic functions

Initially, Dart’s generics could only be used with classes. New syntax _ generic method _, which allows type arguments on methods and functions:

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;
}
Copy the code

The first <T> generic here can use the parameter T as follows:

  • The return value type (T) of the function.
  • Parameter types (List).
  • The type of the local variable (T TMP).