Explore as Sealed Classes em Kotlin

Tempo de leitura: 3 minutes

Problema

Para lidar com os estados de uma solicitação de API, podemos ter usado classes enum. Enum em Java é um tipo de dados que contém um conjunto fixo de constantes. É um tipo de dados especial que permite que uma variável seja um conjunto cheio de constantes predefinidas. Sua variável deve ser igual a um dos valores predefinidos para ela.

public enum Response{ 
SUCCESS, 
FAILURE
}

Aqui não temos nenhum outro detalhe relacionado à falha, apenas especifica se a solicitação falhou ou foi bem-sucedida. Mas se quisermos saber mais sobre o estado de falha, seja uma falha remota, falha de rede, etc., não temos uma solução adequada aqui.

 enum class Response1(msg: String) {
      SUCCESS("SUCESSO"),
      FAILURE(exception:Exception) // Isso não foi permitido
}

Solução

Portanto, para esse tipo de problema, para lidar com os estados junto com os tipos definidos, podemos usar classes seladas como uma boa solução. A classe selada em Kotlin é um novo conceito que não encontramos em Java. Em palavras simples, para melhor manipulação de estado com tipos restritos, podemos fazer uso de classes seladas.

O que são classes seladas?

As classes seladas são usadas para representar hierarquias de classes restritas, quando um valor pode ter um dos tipos de um conjunto limitado, mas não pode ter nenhum outro tipo.

Podemos pensar na classe lacrada como uma extensão das classes enum. Mas os enums têm uma restrição de que cada constante de enum pode ter apenas uma única instância, enquanto uma subclasse de uma classe selada pode ter várias instâncias que podem conter o estado para um melhor tratamento.

Declaração de classe selada – Para declarar uma classe como uma classe selada, precisamos adicionar uma palavra-chave sealed antes da palavra-chave class

sealed class Result

Por que precisamos de classes Seladas?

Como classes abstratas, as classes seladas nos permitem representar hierarquias. As classes filhas podem ser qualquer tipo de classe como classe de dados, classe de objeto, qualquer classe regular ou mesmo outra classe selada.

Mas, ao contrário das classes abstratas, precisamos definir essas hierarquias no mesmo arquivo ou como classes aninhadas. Não podemos estender a classe lacrada fora do arquivo definido, pois isso dá um erro de compilação.

Com classes seladas, podemos ter

  • A liberdade de representação de classes abstratas
  • O conjunto restrito de tipos de enums.

Não podemos herdar de classes enum, mas fazemos isso com classes seladas.

sealed class Result {
    data class Success(val data : DataModel) : Result()
    data class Failure(val exception : Exception) : Result()
}

Implementação

Vamos verificar definindo uma classe selada simples que tem diferentes tipos de classes

sealed class Month {
       class January(var numberOfDays: Int) : Month()
       data class February(var displayName: String) : Month()
       object March : Month() {
           var numberOfDays: Int = 0
           var displayName: String = "March..."
       }
   }

Agora vamos verificar como acessar a classe lacrada criando objetos e passando os valores

fun sampleAccess(month: Month) = when (month) {
        is Month.January -> println("Número de dias em janeiro ${month.numberOfDays}")
        is Month.February -> println("Nome de exibição de fevereiro ${month.displayName}")
        is Month.March -> println("Número de dias e nome de exibição de março ${month.numberOfDays} && ${month.displayName}")
    }

 fun sample() {
        val jan = Month.January(31)
        val fev = Month.February("Fev")

        eval(jan)
        eval(fev)
    }

É melhor usar enums quando temos tipos restritos para usar e não estados associados a cada tipo restrito; no entanto, classes seladas devem ser usadas enquanto temos tipos restritos e estados associados a cada tipo restrito.

Classes seladas para melhor gestão do estado

Com classes seladas, podemos simplificar o gerenciamento de estado relacionado a qualquer classe. Ao usar classes seladas, temos estados predefinidos para melhor gerenciamento. Vamos pensar em um exemplo básico de manipulação de NetworkState com classes seladas com dois estados: Carregando e Falha.

Carregando: este estado retornará um valor booleano. Com base neste valor, decidimos mostrar o carregamento. Se verdadeiro, mostramos o carregamento – caso contrário, está oculto.
Falha: este estado retorna uma mensagem de erro e um código de erro com base nesses parâmetros, podemos tomar uma ação como mostrar pop-up ou tela de nova tentativa, brinde, etc.

sealed class NetworkState {
    data class Loading(var loading : Boolean) : NetworkState()
    data class Failure(var errorMsg : String, var errorCode:String) : NetworkState()
}

Agora, dentro de nossa atividade ou fragmento, podemos usar a expressão when para lidar com todos os estados especificados em NetworkState

when (networkState) {
    is NetworkState.Loading -> {
        loadingStatus(networkState.loading)
    }
    
    is NetworkState.Failure ->{
        handleErrorCase(networkState.errorMsg,networkState.errorCode)
    }
}

Regras de uma aula selada em Kotlin

Como as classes Enum têm suas limitações, as classes seladas também têm algumas condições pré-qualificadas a serem seguidas ao escrevê-las. Vamos examinar o conjunto de regras:

  • Os tipos selados não podem ser instanciados. Não podemos criar o objeto de uma classe lacrada diretamente e podemos ter membros abstratos.
  • Por padrão, o construtor de uma classe selada é privado e não podemos torná-lo não privado.
  • As subclasses de uma classe lacrada devem ser declaradas dentro do mesmo arquivo onde a classe lacrada é declarada, entretanto herdeiros indiretos como classes que estendem subclasses de uma classe lacrada podem ser colocados em qualquer lugar.

Resumo

Agora você tem uma ideia básica do uso de classes seladas.