Sealed Class — o que é, princípio de funcionamento e aplicação

Autor: IT Sectr Publicado: 2026-05-26 Tempo de leitura: 8 min

Sealed Class é um tipo especial de classe em Kotlin que restringe a hierarquia de herança a um conjunto fixo de subtipos. Todos os subtipos são declarados no mesmo arquivo e são conhecidos pelo compilador, o que permite usar um bloco when exaustivo sem uma ramificação else obrigatória. De acordo com Kotlin Docs, 2026, as classes seladas são um mecanismo chave para representar hierarquias limitadas, como estados, tipos de erro e eventos de UI.

Principais pontos

  • Sealed Class — classe com um conjunto fixo de subtipos declarados no mesmo arquivo.
  • Exhaustive when — o compilador verifica que todos os subtipos foram tratados, eliminando ramificações else esquecidas.
  • Sealed interface — Kotlin 1.5+ suporta interfaces seladas para herança múltipla.
  • Hierarquia de erros — sealed class é a forma padrão de tratamento de erros com segurança de tipos em Kotlin.
  • Diferença de enum — cada subtipo de sealed class pode conter estado único e quantidade diferente de campos.

O que é Sealed Class?

Sealed Class (classe selada) é uma classe em Kotlin marcada com o modificador sealed. Ela define uma hierarquia de tipos limitada: todos os subtipos possíveis são listados no mesmo arquivo e o compilador conhece cada um deles. Isso diferencia sealed class de uma classe aberta comum cujos subtipos podem ser declarados em qualquer lugar.

O objetivo principal da sealed class é a representação com segurança de tipos de um conjunto finito de variantes. Cada subtipo pode ter sua própria estrutura de dados, tornando sealed class mais flexível que enum. Em tempo de execução, sealed class é uma classe abstrata comum; o compilador impõe restrições apenas em tempo de compilação.

Sealed class é especialmente útil na arquitetura de aplicativos Android: estados de UI, resultados de requisições de rede, eventos de navegação similares a Intents e, claro, hierarquias de erros são casos de uso típicos.

Em tempo de compilação, sealed class é otimizada em uma tabela de jump para expressões when, tornando-a mais eficiente que cadeias if-else. Combinada com data class, cada subtipo pode conter não apenas estado mas também métodos, permitindo construir modelos de domínio autodocumentados sem código boilerplate.

Sealed class também é eficaz para representar máquinas de estado em aplicativos mobile. Cada estado é um subtipo separado com parâmetros únicos, e as transições entre estados são controladas através de expressões when. O compilador garante que todos os estados possíveis sejam tratados, eliminando erros em tempo de execução ao alterar o estado da UI ou a lógica de negócio.

Sealed Class vs Enum: diferenças principais

Desenvolvedores Kotlin iniciantes frequentemente confundem sealed class com enum, já que ambos restringem o conjunto de valores. No entanto, há uma diferença fundamental: enum é um conjunto de constantes do mesmo tipo, enquanto sealed class é uma hierarquia de tipos diferentes.

Quando escolher enum

Enum é ótimo quando todas as variantes são constantes sem estrutura adicional. Por exemplo, dias da semana, status de pedidos ou tipos de ações sem parâmetros. Cada valor de enum é um singleton com um nome fixo.

Quando escolher sealed class

Sealed class é necessária quando cada variante tem seus próprios dados. Por exemplo, um erro de rede contém um código de resposta, um erro de parsing contém detalhes, e um erro de autorização contém uma mensagem. Cada subtipo de sealed class é um tipo separado com campos únicos.

kotlin
// Enum — todas as variantes de um único tipo
enum class Status { LOADING, SUCCESS, ERROR }

// Sealed class — cada variante com seus próprios dados
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String) : UiState<Nothing>()
}

Sealed Interface vs Sealed Class

Desde Kotlin 1.5, é possível declarar sealed interface. Isso estende o conceito de selamento para interfaces: uma sealed interface também tem um conjunto fixo de implementações, mas suporta herança múltipla.

Quando usar sealed interface

Sealed interface é conveniente quando subtipos precisam implementar múltiplos contratos simultaneamente. Por exemplo, um evento de UI pode ser tanto clicável quanto rastreável. Com sealed class, você teria que escolher uma classe base; com sealed interface, o subtipo implementa ambos.

Limitações da sealed class

Sealed class é uma classe, então cada subtipo só pode ter um pai. Sealed interface resolve esse problema mas não pode conter estado. A escolha entre eles depende da tarefa: se precisa de lógica compartilhada com campos — use sealed class; se precisa de flexibilidade de contratos — use sealed interface.

kotlin
sealed interface ScreenEvent {
    data class Refresh(val force: Boolean) : ScreenEvent
    data class Navigate(val route: String) : ScreenEvent
    data class ShowError(val toast: String) : ScreenEvent
}

sealed interface AnalyticsEvent {
    val name: String
    val params: Map<String, Any>
}

// O subtipo implementa ambas as interfaces
data class LoginClicked(
    override val name: String = "login_click",
    override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent

Sealed Class para hierarquias de erros no desenvolvimento mobile

Um dos principais usos de sealed class no desenvolvimento mobile é a hierarquia de erros com segurança de tipos. Em vez de lançar exceções de diferentes tipos ou usar uma Exception genérica, sealed class coleta todos os erros possíveis de domínio em um único tipo.

Como construir uma hierarquia de erros

Crie uma sealed class DomainError e liste todos os tipos de falha como subtipos. Cada subtipo contém apenas os dados relevantes para aquele tipo específico de erro. O compilador garante que ao tratar o erro você não esquecerá nenhuma variante.

Exemplo: tratamento de erros de autenticação

Considere um aplicativo com autorização onde diferentes cenários de falha são possíveis: senha incorreta, conta bloqueada, problema no servidor. Sealed class os combina em um único tipo com tratamento exaustivo.

kotlin
sealed class AuthError {
    data class InvalidCredentials(
        val attempts: Int
    ) : AuthError()

    data class AccountBlocked(
        val until: Long
    ) : AuthError()

    data class NetworkFailure(
        val cause: Throwable
    ) : AuthError()

    object ServerError : AuthError()
}

fun handleError(error: AuthError): String = when (error) {
    is AuthError.InvalidCredentials ->
        "Tentativas restantes: ${3 - error.attempts}"
    is AuthError.AccountBlocked ->
        "Acesso bloqueado até ${Date(error.until)}"
    is AuthError.NetworkFailure ->
        "Verifique a conexão: ${error.cause.localizedMessage}"
    AuthError.ServerError ->
        "Servidor temporariamente indisponível"
}

Padrões de uso de Sealed Class no Android

Sealed class se tornou uma ferramenta padrão na arquitetura de aplicativos Android. Vamos ver três padrões principais onde sealed class é indispensável no desenvolvimento mobile.

  • UI State — representação da tela como uma máquina de estados: Loading, Content, Error. Cada estado contém seus próprios dados, e sealed class garante que todas as transições sejam tratadas.
  • Navigation Event — sealed class em vez de constantes de navegação: cada tela é um subtipo separado com parâmetros de rota. O compilador verifica os tipos dos argumentos.
  • Action/Intent — o padrão Unidirectional Data Flow usa sealed class para representar todas as ações que um usuário pode realizar em uma tela.

Vale também destacar o uso de sealed class em Clean Architecture. Cada camada (data, domain, presentation) usa sealed class para seus tipos de erro, e os mappers convertem uma sealed class em outra. Por exemplo, DataError da camada de dados é mapeado para DomainError para a lógica de negócio, e então para UiState para a camada de apresentação. Isso preserva a segurança de tipos em todos os níveis do aplicativo e garante que nenhum erro fique sem tratamento.

Testando hierarquias de Sealed Class

Testar sealed class requer uma abordagem especial, já que cada subtipo é um tipo separado com seu próprio estado. Recomenda-se escrever testes parametrizados que percorrem todos os subtipos de sealed class. Isso garante que expressões when cubram todas as variantes, incluindo novas adicionadas ao estender a hierarquia.

Para testes de UI, sealed class como UiState permite verificar a exibição de cada estado: Loading mostra um spinner, Content mostra dados, Error mostra uma mensagem de erro. Como sealed class é finito, a cobertura de teste de todos os estados dá total confiança na correção da lógica de UI.

Erros comuns ao trabalhar com Sealed Class

Apesar da simplicidade do conceito, desenvolvedores cometem erros regularmente ao projetar hierarquias de sealed class. Vamos ver os principais problemas e como evitá-los.

  • Subtipos em arquivos diferentes — o compilador não permitirá declarar uma sealed class se os subtipos estiverem fora do arquivo. Essa restrição garante um when exaustivo.
  • Misturar sealed e open — uma sealed class não pode ser open ao mesmo tempo. Se for necessária uma hierarquia extensível, use uma classe abstrata comum, mas você sacrificará a exaustividade.
  • Aninhamento excessivo — sealed class dentro de sealed class cria uma hierarquia profunda difícil de manter. Para cenários simples, dois níveis são suficientes.
  • Else esquecido no when — se uma sealed class de uma biblioteca não tiver exaustividade, o compilador não avisará sobre um ramo faltante. Adicione else apenas intencionalmente.

Perguntas frequentes

Uma sealed class pode ter métodos abstratos?

Sim, uma sealed class pode conter métodos abstratos, e cada subtipo é obrigado a implementá-los. Isso é conveniente quando todas as variantes devem fornecer uma interface comum mas com lógica de execução diferente.

Sealed classes estão disponíveis em Java?

Em Java 17+, foram introduzidas classes e interfaces seladas com o modificador sealed. O Android atualmente suporta Java 17 parcialmente, mas em projetos Kotlin, sealed class está disponível desde Kotlin 1.0 sem restrições.

Uma sealed class pode herdar de outra sealed class?

Sim, uma sealed class pode ser subtipo de outra. A hierarquia de sealed classes permanece finita: o compilador conhece todos os subtipos em cada nível. Isso permite construir classificações detalhadas de erros.

Sealed class afeta o desempenho?

Sealed class não cria sobrecarga em tempo de execução. O compilador otimiza expressões when com sealed classes em tabelas de jump (tableswitch), que são mais rápidas que cadeias if-else. O desempenho é idêntico ao de enum.

Como testar hierarquias de sealed class?

Cada subtipo de sealed class é testado separadamente. Como sealed class é finito, você pode escrever um teste parametrizado que percorre todas as variantes. Isso fornece cobertura completa dos ramos do bloco when.

Resumo

  • Sealed class — classe com um conjunto fixo de subtipos declarados em um arquivo, permitindo análise exaustiva de when em tempo de compilação.
  • Cada subtipo de sealed class pode ter sua própria estrutura de dados — esta é a principal diferença de enum, onde todas as variantes são constantes do mesmo tipo.
  • Sealed interface (Kotlin 1.5+) suporta herança múltipla, sealed class suporta apenas herança simples. A escolha depende da necessidade de estado compartilhado.
  • Sealed class é o mecanismo padrão para hierarquias de erros com segurança de tipos em Kotlin: cada tipo de falha é um subtipo separado com campos relevantes.
  • Principais padrões no Android: UI State, Navigation Event e Action/Intent — são construídos sobre sealed class para garantir completeza do tratamento.
  • Evite subtipos em arquivos diferentes, aninhamento excessivo e misturar sealed com open — isso viola o contrato de hierarquia finita.

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também