# 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 singletonMap(K key, V value) { return { 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(List 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(int x) => 3; int m(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 thing)) {} // Declares a local generic function `itself`. void localFunction() { T itself(T thing) => thing; } // Binds a generic function expression to a local variable. void functionExpression() { var lambda = (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 thing) { ... } // ^-- Here. ``` * Inside type annotations in the body of the method: ```dart useThing() { T thing = getThing(); //^-- Here. List pair = [thing, thing]; // ^-- And here. } ``` * In the return type of the method: ```dart T itself(T thing) => thing; //^-- Here. ``` * As type arguments in generic classes and method calls: ```dart useThing(T thing) { var pair = [thing, thing]; // ^-- Here. var set = new Set()..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` and `set` will be `Set`. 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(object) { print(object is T); // ^-- Error! print(object is! T); // ^-- Error! } ``` * As a type literal: ```dart printType() { 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 { Iterable map(S transform(T element)) { ... } // Other stuff... } ``` In this example, the type checker: 1. Infers `List` 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`. 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((fruit) => fruit.length).toList(); // So that we can later add doubles to the result. lengths.add(1.2); ```