Menu
Coddy logo textTech

Métodos de Extensão

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy — lição 83 de 110.

Às vezes, você deseja adicionar funcionalidade a uma classe que não possui — como os tipos integrados String ou int do Dart. Você não pode modificar o código-fonte delas, e a herança não ajuda com classes finais. É aqui que entram os métodos de extensão (extension methods).

Extensões permitem que você adicione novos métodos a tipos existentes sem modificá-los ou criar subclasses:

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
}

A sintaxe é extension ExtensionName on Type. Dentro, você define métodos e getters exatamente como em uma classe comum. Uma vez definidos, esses métodos aparecem em todas as instâncias desse tipo como se fossem nativos.

Extensões funcionam em qualquer tipo, incluindo suas próprias classes e 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
}

Observe que dentro de uma extensão, this refere-se à instância na qual o método é chamado. Extensões são uma maneira limpa de aprimorar tipos existentes, mantendo seu código organizado e legível.

challenge icon

Desafio

Fácil

Vamos aprimorar os tipos integrados do Dart com funcionalidades úteis usando métodos de extensão! Você criará extensões que adicionam métodos úteis aos tipos String e List, e depois os utilizará em seu arquivo principal.

Você organizará seu código em dois arquivos:

  • extensions.dart: Crie duas extensões que adicionam novas capacidades a tipos existentes:
    • Uma extensão chamada StringUtils em String com:
      • Um método countVowels() que retorna o número de vogais (a, e, i, o, u - insensível a maiúsculas e minúsculas) na string
      • Um getter initials que retorna a primeira letra de cada palavra (separada por espaços), unidas e em maiúsculas. Por exemplo, "hello world" torna-se "HW"
    • Uma extensão chamada ListUtils<T> em List<T> com:
      • Um método safeGet(int index) que retorna o elemento no índice se ele existir, ou null se o índice estiver fora dos limites
      • Um getter hasMultiple que retorna true se a lista tiver mais de um elemento
  • main.dart: Importe suas extensões e demonstre-as em ação:
    • Crie uma string "Object Oriented Programming" e imprima sua contagem de vogais no formato: Vowels: [count]
    • Imprima as iniciais da mesma string no formato: Initials: [initials]
    • Crie uma lista de inteiros [10, 20, 30]
    • Imprima o resultado de safeGet(1) no formato: Element at 1: [value]
    • Imprima o resultado de safeGet(5) no formato: Element at 5: [value]
    • Imprima se a lista possui múltiplos elementos no formato: Has multiple: [true/false]

Lembre-se de que, dentro de uma extensão, this refere-se à instância na qual o método é chamado. Suas extensões farão com que esses tipos pareçam ter esses métodos integrados!

Saída esperada:

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

Folha de consulta

Métodos de extensão permitem que você adicione novas funcionalidades a tipos existentes sem modificar seu código-fonte ou usar herança.

A sintaxe para criar uma extensão é 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('');
  }
}

Uma vez definidos, os métodos de extensão podem ser chamados em instâncias desse tipo como se fossem nativos:

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

Extensões funcionam em qualquer tipo, incluindo tipos integrados, suas próprias classes e 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 uma extensão, this refere-se à instância na qual o método é chamado.

Experimente você mesmo

import 'extensions.dart';

void main() {
  // Crie a string
  String text = "Object Oriented Programming";

  // TODO: Imprima a contagem de vogais usando countVowels()
  // Formato: Vowels: [count]

  // TODO: Imprima as iniciais usando o getter initials
  // Formato: Initials: [initials]

  // Crie a lista de inteiros
  List<int> numbers = [10, 20, 30];

  // TODO: Imprima o resultado de safeGet(1)
  // Formato: Element at 1: [value]

  // TODO: Imprima o resultado de safeGet(5)
  // Formato: Element at 5: [value]

  // TODO: Imprima se a lista possui múltiplos elementos
  // Formato: Has multiple: [true/false]
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos