Sealed classes resolvem um problema classico em qualquer linguagem: como modelar estados mutuamente exclusivos sem deixar buracos.
Uma sealed class e uma classe que so pode ser herdada por um conjunto fixo de subclasses declaradas no mesmo arquivo.
Veja o exemplo mais comum: modelar estados de uma tela que carrega dados:
sealed class UiState {
object Loading : UiState()
data class Success(val data: List<String>) : UiState()
data class Error(val message: String) : UiState()
}
Sem sealed classes, voce usaria enum ou nullable. Com enum, nao da para carregar dados diferentes em cada estado. Com sealed class, cada estado carrega seus proprios dados com tipos diferentes.
Uso com when exaustivo:
fun render(state: UiState) {
when (state) {
is UiState.Loading -> mostrarLoading()
is UiState.Success -> mostrarDados(state.data)
is UiState.Error -> mostrarErro(state.message)
}
}
O when e exaustivo. Se voce adicionar um novo estado e nao tratar, o codigo nao compila. Isso elimina um monte de bugs de estado esquecido.
Outro uso comum: modelar o resultado de uma operacao:
sealed class Resultado<out T> {
data class Sucesso<T>(val dados: T) : Resultado<T>()
data class Falha(val erro: String) : Resultado<Nothing>()
object Cancelado : Resultado<Nothing>()
}
fun buscarUsuario(id: Int): Resultado<Usuario> {
return try {
val usuario = api.getUsuario(id)
Resultado.Sucesso(usuario)
} catch (e: Exception) {
Resultado.Falha(e.message ?: "erro desconhecido")
}
}
when (val res = buscarUsuario(123)) {
is Resultado.Sucesso -> println(res.dados.nome)
is Resultado.Falha -> println("erro: ${res.erro}")
is Resultado.Cancelado -> println("operacao cancelada")
}
Sealed classes tambem funcionam bem com sealed interfaces. A diferenca e que a interface permite multiplas herancas:
sealed interface Acao
class Navegar(val tela: String) : Acao
class ExibirMensagem(val texto: String) : Acao
object Voltar : Acao
fun executar(acao: Acao) = when (acao) {
is Navegar -> navegarPara(acao.tela)
is ExibirMensagem -> mostrar(acao.texto)
is Voltar -> finish()
}
Uma dica: combine sealed classes com data classes para ter imutabilidade e comparacao por valor:
sealed class PedidoState {
object Pendente : PedidoState()
object Processando : PedidoState()
data class Concluido(val id: String, val total: Double) : PedidoState()
data class Cancelado(val motivo: String) : PedidoState()
}
fun processar(state: PedidoState) {
if (state is PedidoState.Concluido) {
println("Pedido ${state.id} no valor de ${state.total}")
}
}
O sealed elimina a necessidade de tratar estados invalidos. Se nao pode estar loading e sucesso ao mesmo tempo, a sealed class garante isso em tempo de compilacao.
That's all for now.
Thanks for reading!
Top comments (0)