| # Using Generic Methods |
| |
| **Note: For historical reasons, this feature is called "generic methods", but it |
| applies equally well to instance methods, static methods, top-level functions, |
| local functions, and even lambda expressions.** |
| |
| Initially a [proposal][], generic methods are on their way to being fully |
| supported in Dart. Here is how to use them. |
| |
| [proposal]: https://github.com/leafpetersen/dep-generic-methods/blob/master/proposal.md |
| |
| When they were still being prototyped, an [older comment-based syntax was |
| designed][old] so that the static analysis could be implemented and tested |
| before the VM and compilers needed to worry about the syntax. Now that real |
| syntax is allowed everywhere, this doc has been updated. |
| |
| [old]: GENERIC_METHOD_COMMENTS.md |
| |
| ## Declaring generic methods |
| |
| Type parameters for generic methods are listed after the method or function |
| name, inside angle brackets: |
| |
| ```dart |
| /// Takes two type parameters, [K] and [V]. |
| Map<K, V> singletonMap<K, V>(K key, V value) { |
| return <K, V>{ key, value }; |
| } |
| ``` |
| |
| As with classes, you can put bounds on type parameters: |
| |
| ```dart |
| /// Takes a list of two numbers of some num-derived type [T]. |
| T sumPair<T extends num>(List<T> items) { |
| return items[0] + items[1]; |
| } |
| ``` |
| |
| Class methods (instance and static) can be declared to take generic parameters |
| in the same way: |
| |
| ```dart |
| class C { |
| static int f<S, T>(int x) => 3; |
| int m<S, T>(int x) => 3; |
| } |
| ``` |
| |
| This even works for function-typed parameters, local functions, and function |
| expressions: |
| |
| ```dart |
| /// Takes a generic method as a parameter [callback]. |
| void functionTypedParameter(T callback<T>(T thing)) {} |
| |
| // Declares a local generic function `itself`. |
| void localFunction() { |
| T itself<T>(T thing) => thing; |
| } |
| |
| // Binds a generic function expression to a local variable. |
| void functionExpression() { |
| var lambda = <T>(T thing) => thing; |
| } |
| ``` |
| |
| We do not currently support a way to declare a function as *returning* a generic |
| function. This will eventually be supported using a `typedef`. |
| |
| ## Using generic method type parameters |
| |
| You've seen some examples already, but you can use a generic type parameter |
| almost anywhere you would expect in a generic method. |
| |
| * Inside the method's parameter list: |
| |
| ```dart |
| takeThing<T>(T thing) { ... } |
| // ^-- Here. |
| ``` |
| |
| * Inside type annotations in the body of the method: |
| |
| ```dart |
| useThing<T>() { |
| T thing = getThing(); |
| //^-- Here. |
| List<T> pair = [thing, thing]; |
| // ^-- And here. |
| } |
| ``` |
| |
| * In the return type of the method: |
| |
| ```dart |
| T itself<T>(T thing) => thing; |
| //^-- Here. |
| ``` |
| |
| * As type arguments in generic classes and method calls: |
| |
| ```dart |
| useThing<T>(T thing) { |
| var pair = <T>[thing, thing]; |
| // ^-- Here. |
| var set = new Set<T>()..add(thing); |
| // ^-- And here. |
| } |
| ``` |
| |
| Note that generic methods are not yet supported *at runtime* on the VM and |
| dart2js. On those platforms, uses of generic method type arguments are |
| treated like `dynamic` today. So in this example, `pair`'s reified type at |
| runtime will be `List<dynamic>` and `set` will be `Set<dynamic>`. |
| |
| There are two places you *cannot* use a generic method type parameter. Both are |
| because the VM and dart2js don't support reifying generic method type arguments |
| yet. Since these expressions wouldn't do what you want, we've temporarily |
| defined them to be an error: |
| |
| * As the right-hand side of an `is` or `is!` expression. |
| |
| ```dart |
| testType<T>(object) { |
| print(object is T); |
| // ^-- Error! |
| print(object is! T); |
| // ^-- Error! |
| } |
| ``` |
| |
| * As a type literal: |
| |
| ```dart |
| printType<T>() { |
| Type t = T; |
| // ^-- Error! |
| print(t); |
| } |
| ``` |
| |
| Once we have full runtime support for generic methods, these will be allowed. |
| |
| ## Calling generic methods |
| |
| Most of the time, when you call a generic method, you can leave off the type |
| arguments and strong mode's type inference will fill them in for you |
| automatically. For example: |
| |
| ```dart |
| var fruits = ["apple", "banana", "cherry"]; |
| var lengths = fruits.map((fruit) => fruit.length); |
| ``` |
| |
| The `map()` method on Iterable is now generic and takes a type parameter for the |
| element type of the returned sequence: |
| |
| ```dart |
| class Iterable<T> { |
| Iterable<S> map<S>(S transform(T element)) { ... } |
| |
| // Other stuff... |
| } |
| ``` |
| |
| In this example, the type checker: |
| |
| 1. Infers `List<String>` for the type of `fruits` based on the elements in the |
| list literal. |
| 2. That lets it infer `String` for the type of the lambda parameter `fruit` |
| passed to `map()`. |
| 3. Then, from the result of calling `.length`, it infers the return type of the |
| lambda to be `int`. |
| 4. That in turn is used to fill in the type argument to the call to `map()` as |
| `int`, and the resulting sequence is an `Iterable<int>`. |
| |
| If inference *isn't* able to fill in a type argument for you, it uses `dynamic` |
| instead. If that isn't what you want, or it infers a type you don't want, you |
| can always pass them explicitly: |
| |
| ```dart |
| // Explicitly give a type so that we don't infer "int". |
| var lengths = fruits.map<num>((fruit) => fruit.length).toList(); |
| |
| // So that we can later add doubles to the result. |
| lengths.add(1.2); |
| ``` |