Menu
Coddy logo textTech

Classes Seladas (Dart 3)

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

O Dart 3 introduziu classes seladas (sealed classes), que restringem quais classes podem estendê-las ou implementá-las. Quando você marca uma classe como sealed, apenas classes no mesmo arquivo podem ser seus subtipos diretos. Isso cria um conjunto fechado de subtipos conhecidos.

sealed class Result {}

class Success extends Result {
  final String data;
  Success(this.data);
}

class Failure extends Result {
  final String error;
  Failure(this.error);
}

O poder das classes seladas vem da verificação de exaustividade. Quando você utiliza um switch em um tipo selado, o compilador conhece todos os subtipos possíveis e garante que você trate cada um deles:

String handleResult(Result result) {
  return switch (result) {
    Success(data: var d) => 'Got: $d',
    Failure(error: var e) => 'Error: $e',
  };
  // Nenhum padrão necessário - o compilador sabe que todos os casos estão cobertos!
}

void main() {
  var success = Success('Hello');
  var failure = Failure('Not found');
  
  print(handleResult(success));  // Got: Hello
  print(handleResult(failure));  // Error: Not found
}

Se você adicionar um novo subtipo a uma sealed class, o compilador sinalizará cada instrução switch que não o trate. Isso torna sealed classes ideais para modelar estados, resultados ou qualquer cenário onde você tenha um conjunto fixo de variantes. Ao contrário de abstract classes, sealed classes garantem que nenhum código externo possa adicionar subtipos inesperados.

challenge icon

Desafio

Fácil

Vamos construir um manipulador de requisições de rede usando classes seladas (sealed classes)! Você criará um sistema com segurança de tipos que modela diferentes estados de uma resposta de rede, garantindo que cada resultado possível seja tratado adequadamente.

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

  • response.dart: Crie uma hierarquia de classes seladas que represente todos os estados possíveis de uma resposta de rede:
    • Uma classe selada chamada NetworkResponse que serve como base para todos os tipos de resposta
    • Uma classe Loading que estende NetworkResponse com um campo String message (ex: "Fetching data...")
    • Uma classe Success que estende NetworkResponse com um campo String data e um campo int statusCode
    • Uma classe Error que estende NetworkResponse com um campo String errorMessage e um campo int errorCode
  • main.dart: Importe seu arquivo de resposta e crie uma função que trate todos os tipos de resposta usando uma expressão switch:
    • Crie uma função handleResponse(NetworkResponse response) que retorna uma String
    • Use uma expressão switch com correspondência de padrões (pattern matching) para tratar cada caso:
      • Para Loading: retorne Status: [message]
      • Para Success: retorne Success ([statusCode]): [data]
      • Para Error: retorne Error ([errorCode]): [errorMessage]
    • Crie três instâncias de resposta e imprima o resultado do tratamento de cada uma:
      • Um Loading com a mensagem Please wait...
      • Um Success com os dados User profile loaded e o código de status 200
      • Um Error com a mensagem de erro Not found e o código de erro 404

A beleza das classes seladas é que o compilador garante que você trate todos os tipos de resposta possíveis - nenhum caso padrão (default) é necessário!

Saída esperada:

Status: Please wait...
Success (200): User profile loaded
Error (404): Not found

Folha de consulta

Classes seladas (sealed classes) restringem quais classes podem estendê-las ou implementá-las. Apenas classes no mesmo arquivo podem ser subtipos diretos, criando um conjunto fechado de variantes conhecidas.

sealed class Result {}

class Success extends Result {
  final String data;
  Success(this.data);
}

class Failure extends Result {
  final String error;
  Failure(this.error);
}

Classes seladas permitem a verificação de exaustividade em expressões switch. O compilador conhece todos os subtipos possíveis e garante que você trate cada um deles:

String handleResult(Result result) {
  return switch (result) {
    Success(data: var d) => 'Got: $d',
    Failure(error: var e) => 'Error: $e',
  };
  // No default case needed - all cases are covered
}

Se você adicionar um novo subtipo, o compilador sinaliza cada instrução switch que não o trata. Isso torna as classes seladas ideais para modelar estados, resultados ou qualquer conjunto fixo de variantes onde você deseja garantias em tempo de compilação de que todos os casos sejam tratados.

Experimente você mesmo

import 'response.dart';

// TODO: Crie uma função handleResponse(NetworkResponse response)
// que retorna uma String
// Use uma switch expression com pattern matching para lidar com cada caso:
// - Para Loading: retorne "Status: [message]"
// - Para Success: retorne "Success ([statusCode]): [data]"
// - Para Error: retorne "Error ([errorCode]): [errorMessage]"

void main() {
  // TODO: Crie uma instância de Loading com a mensagem "Please wait..."
  
  // TODO: Crie uma instância de Success com os dados "User profile loaded" e o código de status 200
  
  // TODO: Crie uma instância de Error com a mensagem de erro "Not found" e o código de erro 404
  
  // TODO: Imprima o resultado do processamento de cada resposta
}
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