Extension methods

    When you’re using someone else’s API orwhen you implement a library that’s widely used,it’s often impractical or impossible to change the API.But you might still want to add some functionality.

    For example, consider the following code that parses a string into an integer:

    It might be nice — shorter and easier to use with tools — tohave that functionality be on instead:

    1. '42'.parseInt()

    To enable that code,you can import a library that contains an extension of the String class:

    1. import 'string_apis.dart';
    2. ...
    3. print('42'.parseInt());

    Extensions can define not just methods,but also other members such as getter, setters, and operators.Also, extensions have names, which can be helpful if an API conflict arises.Here’s how you might implement the extension method parseInt(),using an extension (named NumberParsing) that operates on strings:

    1. extension NumberParsing on String {
    2. int parseInt() {
    3. return int.parse(this);
    4. }
    5. }

    The next section describes how to use extension methods.After that are sections about implementing extension methods.

    Like all Dart code, extension methods are in libraries.You’ve already seen how to use an extension method —just import the library it’s in, and use it like an ordinary method:

    You can’t invoke extension methods on variables of type dynamic.For example, the following code results in a runtime exception:

    1. dynamic d = '2';
    2. print(d.parseInt()); // Runtime exception: NoSuchMethodError

    Extension methods do work with Dart’s type inference.The following code is fine becausethe variable v is inferred to have type String:

    1. var v = '2';

    The reason that dynamic doesn’t work is thatextension methods are resolved against the static type of the receiver.Because extension methods are resolved statically,they’re as fast as calling a static function.

    For more information about static types and dynamic, see.

    API conflicts

    If an extension member conflicts withan interface or with another extension member,then you have a few options.

    One option is changing how you import the conflicting extension,using or hide to limit the exposed API:

    1. // Defines the String extension method parseInt().
    2. import 'string_apis.dart';
    3. // Also defines parseInt(), but hiding NumberParsing2
    4. // hides that extension method.
    5. import 'string_apis_2.dart' hide NumberParsing2;
    6. ...
    7. // Uses the parseInt() defined in 'string_apis.dart'.
    8. print('42'.parseInt());

    Another option is applying the extension explicitly,which results in code that looks as if the extension is a wrapper class:

    1. // Both libraries define extensions named NumberParsing
    2. // that contain the extension method parseInt(). One NumberParsing
    3. // extension (in 'string_apis_3.dart') also defines toNum().
    4. import 'string_apis.dart';
    5. import 'string_apis_3.dart' as rad;
    6. ...
    7. // print('42'.parseInt()); // Doesn't work.
    8. // Use the NumberParsing extension from string_apis.dart.
    9. print(NumberParsing('42').parseInt());
    10. print(rad.NumberParsing('42').parseInt());
    11. // Only string_apis_3.dart has toNum().
    12. print('42'.toNum());

    As the example shows,you can invoke extension methods implicitly even if you import using a prefix.The only time you need to use the prefix isto avoid a name conflict when invoking an extension explicitly.

    Use the following syntax to create an extension:

    1. extension <extension name> on <type> {
    2. (<member definition>)*
    3. }

    For example, here’s how you might implement an extension of the String class:

    1. // In string_apis.dart:
    2. extension NumberParsing on String {
    3. int parseInt() {
    4. return int.parse(this);
    5. }
    6. double parseDouble() {
    7. return double.parse(this);
    8. }
    9. }

    To create a local extension that’s visible only inthe library where it’s declared,either omit the extension name or give it a namethat starts with an underscore (_).

    The members of the extension can be methods, getters, setters, operators.Extensions can also have static fields and static helper methods.

    Extensions can have generic type parameters.For example, here’s some code that extends the built-in List<T> typewith a getter, an operator, and a method:

    The type T is bound based on the static type of the list thatthe methods are called on.