Menu
Coddy logo textTech

Métodos de extensión

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 83 de 110.

A veces quieres añadir funcionalidad a una clase que no te pertenece, como los tipos integrados String o int de Dart. No puedes modificar su código fuente, y la herencia no ayuda con las clases finales. Aquí es donde entran en juego los métodos de extensión.

Las extensiones te permiten añadir nuevos métodos a tipos existentes sin modificarlos ni crear subclases:

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

void main() {
  print('hello'.reverse());        // olleh
  print('radar'.isPalindrome);     // true
}

La sintaxis es extension ExtensionName on Type. En el interior, defines métodos y getters al igual que en una clase normal. Una vez definidos, estos métodos aparecen en todas las instancias de ese tipo como si estuvieran integrados.

Las extensiones funcionan en cualquier tipo, incluyendo tus propias clases y tipos genéricos:

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

void main() {
  print(5.squared());                    // 25
  print([1, 2, 3].secondOrNull);         // 2
}

Ten en cuenta que dentro de una extensión, this se refiere a la instancia sobre la cual se llama al método. Las extensiones son una forma limpia de mejorar los tipos existentes mientras mantienes tu código organizado y legible.

challenge icon

Desafío

Fácil

¡Vamos a mejorar los tipos integrados de Dart con funcionalidades útiles utilizando métodos de extensión! Crearás extensiones que añaden métodos prácticos a los tipos String y List, y luego los usarás en tu archivo principal.

Organizarás tu código en dos archivos:

  • extensions.dart: Crea dos extensiones que añadan nuevas capacidades a los tipos existentes:
    • Una extensión llamada StringUtils sobre String con:
      • Un método countVowels() que devuelva el número de vocales (a, e, i, o, u - sin distinguir entre mayúsculas y minúsculas) en la cadena
      • Un getter initials que devuelva la primera letra de cada palabra (separada por espacios), unidas y en mayúsculas. Por ejemplo, "hello world" se convierte en "HW"
    • Una extensión llamada ListUtils<T> sobre List<T> con:
      • Un método safeGet(int index) que devuelva el elemento en el índice si existe, o null si el índice está fuera de los límites
      • Un getter hasMultiple que devuelva true si la lista tiene más de un elemento
  • main.dart: Importa tus extensiones y demuéstralas en acción:
    • Crea una cadena "Object Oriented Programming" y muestra su recuento de vocales en el formato: Vowels: [count]
    • Muestra las iniciales de la misma cadena en el formato: Initials: [initials]
    • Crea una lista de enteros [10, 20, 30]
    • Muestra el resultado de safeGet(1) en el formato: Element at 1: [value]
    • Muestra el resultado de safeGet(5) en el formato: Element at 5: [value]
    • Muestra si la lista tiene múltiples elementos en el formato: Has multiple: [true/false]

Recuerda que dentro de una extensión, this se refiere a la instancia sobre la que se llama al método. ¡Tus extensiones harán que estos tipos parezcan tener estos métodos integrados!

Resultado esperado:

Vowels: 9
Initials: OOP
Element at 1: 20
Element at 5: null
Has multiple: true

Hoja de referencia

Los métodos de extensión le permiten agregar nueva funcionalidad a tipos existentes sin modificar su código fuente o utilizar la herencia.

La sintaxis para crear una extensión es extension ExtensionName on Type:

extension StringExtras on String {
  String reverse() {
    return split('').reversed.join('');
  }
  
  bool get isPalindrome {
    var cleaned = toLowerCase().replaceAll(' ', '');
    return cleaned == cleaned.split('').reversed.join('');
  }
}

Una vez definidos, los métodos de extensión se pueden llamar en instancias de ese tipo como si fueran integrados:

print('hello'.reverse());        // olleh
print('radar'.isPalindrome);     // true

Las extensiones funcionan en cualquier tipo, incluyendo tipos integrados, sus propias clases y tipos genéricos:

extension IntExtras on int {
  int squared() => this * this;
}

extension ListExtras<T> on List<T> {
  T? get secondOrNull => length >= 2 ? this[1] : null;
}

print(5.squared());                    // 25
print([1, 2, 3].secondOrNull);         // 2

Dentro de una extensión, this se refiere a la instancia sobre la cual se llama al método.

Pruébalo tú mismo

import 'extensions.dart';

void main() {
  // Crea la cadena
  String text = "Object Oriented Programming";

  // TODO: Imprime el recuento de vocales usando countVowels()
  // Formato: Vowels: [count]

  // TODO: Imprime las iniciales usando el getter initials
  // Formato: Initials: [initials]

  // Crea la lista de enteros
  List<int> numbers = [10, 20, 30];

  // TODO: Imprime el resultado de safeGet(1)
  // Formato: Element at 1: [value]

  // TODO: Imprime el resultado de safeGet(5)
  // Formato: Element at 5: [value]

  // TODO: Imprime si la lista tiene múltiples elementos
  // Formato: Has multiple: [true/false]
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos