Extension methods add functionality to existing libraries. You might use extension methods without even knowing it. For example, when you use code completion in an IDE, it suggests extension methods alongside regular methods.
For example, consider the following code that parses a string into an integer:
int.parse('42')
It might be nice—shorter and easier to use with tools—to have that functionality be on String
instead:
'42'.parseInt()
To enable that code, you can import a library that contains an extension of the String
class:
import 'string_apis.dart';
// ···
print('42'.parseInt()); // Use an extension method.
Extensions can define not just methods, but also other members such as getter, setters, and operators. Also, extensions can 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:
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
// ···
}
The next section describes how to use extension methods. After that are sections about implementing extension methods.
Using 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:
// Import a library that contains an extension on String.
import 'string_apis.dart';
// ···
print('42'.padLeft(5)); // Use a String method.
print('42'.parseInt()); // Use an extension method.
That’s all you usually need to know to use extension methods. As you write your code, you might also need to know how extension methods depend on static types (as opposed to dynamic
) and how to resolve API conflicts.
Static types and dynamic
You can’t invoke extension methods on variables of type dynamic
. For example, the following code results in a runtime exception:
dynamic d = '2';
print(d.parseInt()); // Runtime exception: NoSuchMethodError
Extension methods do work with Dart’s type inference. The following code is fine because the variable v
is inferred to have type String
:
var v = '2';
print(v.parseInt()); // Output: 2
The reason that dynamic
doesn’t work is that extension 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.
Implementing extension methods
Use the following syntax to create an extension:
extension <extension name>? on <type> {
(<member definition>)*
}
For example, here’s how you might implement an extension on the String
class:
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
The members of an extension can be methods, getters, setters, or operators. Extensions can also have static fields and static helper methods.
Unnamed extensions
When declaring an extension, you can omit the name. Unnamed extensions are visible only in the library where they’re declared. Since they don’t have a name, they can’t be explicitly applied to resolve API conflicts.
extension on String {
bool get isBlank => trim().isEmpty;
}
Implementing generic extensions
Extensions can have generic type parameters. For example, here’s some code that extends the built-in List<T>
type with a getter, an operator, and a method:
extension MyFancyList<T> on List<T> {
int get doubleLength => length * 2;
List<T> operator -() => reversed.toList();
List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}
The type T
is bound based on the static type of the list that the methods are called on.