Los métodos de extensión permiten añadir nuevas funcionalidades a tipos existentes y tipos de bibliotecas de terceros sin modificar el código fuente de la clase original. El autocompletado del IDE mostrará los métodos de extensión junto a los métodos nativos.
No puedes modificar las APIs proporcionadas por otros desarrolladores, pero quieres incorporar métodos auxiliares prácticos.
Ejemplo de requisito: usar '42'.parseInt() en lugar de int.parse('42').
Estructura sintáctica
extension NombreExtensión? on TipoObjetivo {
// Métodos, getters, setters, operadores, miembros estáticos
}Lenguaje del código: JavaScript (javascript)
Ejemplo
// Todo el código en un único archivo, no requiere separación, main se ejecuta directamente
extension NumberParsing on String {
int parseInt() {
return int.parse(this);
}
double parseDouble() {
return double.parse(this);
}
}
void main() {
print('42'.parseInt()); // 42
print('3.14'.parseDouble()); // 3.14
print('100'.padLeft(6)); // Comparación con método nativo de String
}Lenguaje del código: Dart (dart)
Resultado de ejecución
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
42
3.14
100Lenguaje del código: CSS (css)
El nombre de la extensión es opcional; this hace referencia a la instancia que invoca la extensión.
Tipos de miembros soportados en extensiones
Dentro de una extensión puedes definir: métodos de instancia, getters, setters, operadores y miembros estáticos.
extension StringExt on String {
// getter
bool get isEmptyOrBlank => trim().isEmpty;
// Método de instancia
String repeat(int count) => List.filled(count, this).join();
// Sobrecarga de operadores
String operator *(int count) => repeat(count);
// Miembro estático (se accede mediante el nombre de la extensión)
static String emptyStr = '';
static void printTip() => print("Método estático de la extensión de cadena");
}
void main() {
var str = " dart ";
print(str.isEmptyOrBlank);
print("hi" * 3);
print("hello".repeat(2));
// Acceder a miembros estáticos
print(StringExt.emptyStr);
StringExt.printTip();
}Lenguaje del código: PHP (php)
Resultado de ejecución
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
false
hihihi
hellohello
Método estático de la extensión de cadenaLenguaje del código: CSS (css)
Extensiones sin nombre (extensiones anónimas)
Si omites el nombre de la extensión, esta solo será visible dentro de la biblioteca actual. No se pueden resolver conflictos mediante invocación explícita.
No se puede invocar desde el exterior usando la sintaxis NombreExtensión(instancia).
extension on String {
bool get isBlank => trim().isEmpty;
}
void main() {
print(" ".isBlank); // true
}Lenguaje del código: Dart (dart)
Los miembros estáticos de una extensión anónima solo pueden usarse dentro de la propia extensión.
Extensiones genéricas (Generic extensions)
Puedes crear extensiones para tipos genéricos como List<T>
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)];
}
void main() {
List<int> list = [1, 2, 3, 4];
print(list.doubleLength); // 8
print(-list); // [4,3,2,1]
print(list.split(2)); // [[1,2],[3,4]]
}Lenguaje del código: Dart (dart)
Resultado de ejecución
D:\dartdemo\firstdart>dart run
Building package executable...
Built firstdart:firstdart.
8
[4, 3, 2, 1]
[[1, 2], [3, 4]]Lenguaje del código: CSS (css)
Tipo estático static frente a dynamic
Las variables dynamic no pueden llamar a métodos de extensión
Los métodos de extensión se resuelven de forma estática. El tipo de una variable dynamic se define en tiempo de ejecución, por lo que no puede localizar las extensiones:
void main() {
dynamic d = '2';
// print(d.parseInt()); // Provoca NoSuchMethodError en tiempo de ejecución
var v = '2';
print(v.parseInt()); // El tipo se infiere como String y funciona correctamente
}
extension NumberParsing on String {
int parseInt() => int.parse(this);
}Lenguaje del código: JavaScript (javascript)
Motivo: Los métodos de extensión se enlazan en tiempo de compilación según el tipo estático de la variable, no mediante búsqueda en tiempo de ejecución.
Soluciones para conflictos de nombres en APIs
Existen tres soluciones cuando varias extensiones tienen miembros con el mismo nombre.
1: Ocultar extensiones conflictivas usando show / hide al importar
// string_apis.dart contiene NumberParsing
// string_apis_2.dart contiene NumberParsing2, también con el método parseInt
import 'string_apis.dart';
import 'string_apis_2.dart' hide NumberParsing2;
void main() {
print('42'.parseInt()); // Usa la implementación de string_apis.dart
}Lenguaje del código: Dart (dart)
2: Invocación explícita de la extensión (sintaxis envoltorio)
import 'string_apis.dart'; // NumberParsing
import 'string_apis_2.dart'; // NumberParsing2
void main() {
// print('42'.parseInt()); // Conflicto, error de compilación
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());
}Lenguaje del código: Dart (dart)
3: Importar bibliotecas con prefijo
Usa prefijos para diferenciar dos extensiones con nombre idéntico:
import 'string_apis.dart';
import 'string_apis_3.dart' as rad;
void main() {
print(NumberParsing('42').parseInt());
print(rad.NumberParsing('42').parseInt());
// Los métodos de extensión sin conflictos se pueden invocar implícitamente
print('42'.parseNum());
}Lenguaje del código: Dart (dart)
Métodos de extensión (Extension methods)