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.
Desafio
FácilVamos 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
NetworkResponseque serve como base para todos os tipos de resposta - Uma classe
Loadingque estendeNetworkResponsecom um campoString message(ex: "Fetching data...") - Uma classe
Successque estendeNetworkResponsecom um campoString datae um campoint statusCode - Uma classe
Errorque estendeNetworkResponsecom um campoString errorMessagee um campoint errorCode
- Uma classe selada chamada
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 umaString - Use uma expressão switch com correspondência de padrões (pattern matching) para tratar cada caso:
- Para
Loading: retorneStatus: [message] - Para
Success: retorneSuccess ([statusCode]): [data] - Para
Error: retorneError ([errorCode]): [errorMessage]
- Para
- Crie três instâncias de resposta e imprima o resultado do tratamento de cada uma:
- Um
Loadingcom a mensagemPlease wait... - Um
Successcom os dadosUser profile loadede o código de status200 - Um
Errorcom a mensagem de erroNot founde o código de erro404
- Um
- Crie uma função
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 foundFolha 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
}
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
1Fundamentos de POO
Arquivos ExternosBibliotecas e ImportaçõesIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosVariáveis de InstânciaFundamentos de ConstrutoresRecapitulação - Calculadora Simples4Null Safety
Introdução ao Null SafetyNullable vs Non-NullableOs Operadores ? e !Palavra-chave Late e Null SafetyOperadores Null-AwareNull Safety em ClassesRecapitulação - Sistema de Perfil de Usuário7Classes Abstratas e Interfaces
Classes AbstratasMétodos AbstratosInterfaces em DartInterfaces ImplícitasImplementando vs EstendendoMúltiplas InterfacesRecapitulação - Calculadora de Formas10Coleções e Generics
Visão Geral de List, Set e MapColeções Type-SafeClasses GenéricasMétodos GenéricosRestrições de GenericsIterable e IteratorRecapitulação - Armazenamento Genérico13Conceitos Avançados de POO
Composição vs HerançaMétodos de ExtensãoClasses ChamáveisClasses Seladas (Dart 3)Records (Dart 3)Patterns & Matching (3.0)Enums com Métodos16Projeto: Gerenciamento de Biblioteca
Visão Geral do ProjetoClasses Livro e Usuário2Construtores em Dart
Construtor PadrãoConstrutores NomeadosListas de InicializaçãoConstrutores ConstantesConstrutores FactoryConstrutores de RedirecionamentoRecapitulação - Shape Builder5Encapsulamento
Membros Públicos vs PrivadosA Convenção do Prefixo _Privacidade em Nível de BibliotecaAprofundamento em Getters & SettersOcultação de InformaçãoRecapitulação - Registros de Estudantes8Mixins
Introdução aos MixinsCriando MixinsUsando Múltiplos MixinsPalavra-chave on em MixinsMixin vs HerançaMixin vs InterfaceRecapitulação - Sistema Animal11Métodos Especiais
Sobrescrita de toString()Sobrescrita de hashCode & ==Interface ComparableMétodo call()Sobrescrita de noSuchMethodRecapitulação - Coleção Personalizada14Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão ObserverPadrão Strategy3Propriedades de Classe
Membros de Instância vs EstáticosCampos Final & ConstVariáveis LateMétodos e Campos EstáticosGetters e SettersRecapitulação - Gerenciador de Conta Bancária6Herança
Herança BásicaA Palavra-chave superSobrescrita de MétodoA Anotação @overrideA Palavra-chave final para ClassesConstrutores e HerançaRecapitulação - Hierarquia de Funcionários9Polimorfismo
Fundamentos do PolimorfismoPolimorfismo via InterfacesVerificação de Tipos em Tempo de ExecuçãoOs Operadores is & asPalavra-chave CovariantRecapitulação - Processador de Pagamentos12POO Assíncrona
Futures e async/awaitFundamentos de StreamsStream ControllersConstrutores AssíncronosAsync em Métodos de ClasseRecapitulação - Data Fetcher15Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Repository