NullSafety e Exceções

NullSafety e Exceções

Observação: Os arquivos utilizados neste conteúdo que falam sobre NullSafety e Exceções são:

Neste conteúdo iremos aprender sobre o uso de nulos seguros, e como tratar exceções em nosso código.

O que é Null Safety?

Assim como em toda linguagem de programação, nós temos um tipo de dado chamado null, que significa "nulo".

Usamos ele quando queremos apontar para um objeto ou endereço inexistente.

Seja porque queremos fazer validações mais tarde, ou porque estamos instanciando uma variável nula onde mais tarde iremos atribuir um valor a ela, tal valor que ainda não sabemos.

No Kotlin para declararmos uma variável nula, precisamos inserir um ponto de interrogação no final do seu tipo, observe:

var s: String? = null//Com o ponto de interrogação, podemos atribuir nulo a variável (seja ela de qualquer tipo)

var a: Int? = null//Tipo Int que aceita null

//Todos os tipos de dados primitivos aceitam Null quando são declarados junto com o '?'

É importante ressaltar que não conseguimos atribuir um valor nulo a variáveis que não aceitam esse tipo de valor:

var s: String = null//Não conseguimos atribuir nulo a variáveis não nulas

Quando usamos o ponto de interrogação, o compilador não gera uma exceção. Isso é o Kotlin fazendo o tratamento de nulos para nós:

println(s?.length)////Quando usamos o ponto de interrogação, o compilador não gera uma exceção. Isso é o Kotlin fazendo o tratamento do nulo para nós

Quando usamos dois pontos de exclamação, estou dizendo ao Kotlin que iremos tratar esse erro de compilação no nosso código caso a variável tiver um valor nulo:

println(s!!.length)//"Não trate, eu tenho certeza que a variável não é nula, e caso for, eu assumo o problema"

Se tentarmos usar o println em uma variável do tipo nula, o compilador não deixará executar o nosso projeto: 

println(s.length)//Se colocar de forma solta, o compilador não roda o projeto
  • Quando usamos o ? depois da varíavel, dizemos ao Kotlin para tratar o Null caso aja algum problema.
  • Quando usamos o !! depois da variável, diz ao Kotlin que nós iremos assumir o erro.

readlnOrNull()

Como vimos no conteúdo que fala sobre Leitura de Dados, nós podemos ler os dados que o usuário digita no console usando o readln().

No caso do readlnOrNull(), ele nos ajuda a tratar esse dado quando o usuário envia uma resposta vazia.

 var texto = readlnOrNull()//Aqui estamos tratando caso venha nulo. Perceba que o IntelliJ, já reconhece essa variável como 'String?'

if(texto == null || texto == ""){//Estamos verificando se o valor veio nulo
println("Voce não digitou nada no console")
}

O uso do Let

let nada mais é do que um bloco de código que podemos atrelar a uma determinada variável, onde este bloco só será executado caso a variável for diferente de nula.

Para que possamos entender melhor, observe como faríamos isso usando a estrutura do IF/ELSE:

val textor: String? = null//Criamos uma variável chamada 'textorque armazena um valor nulo

if(textor != null){//Verificamos se o conteúdo de textor é diferente de nulo
//Em caso positivo, ele executa as próximas linhas abaixo:
println(textor.lowercase())
println(textor.length)
}

Com o uso do let, o comando acima poderia ser reduzido a isto:

//Observe como let funciona

textor?.let{
//O corpo do let so é executado caso o textor for diferente de nulo
//Aqui dentro é uma espécie de escopo de função
//nesse caso a variável passa a se chamar de it
println(it.lowercase())
println(it.length)
}//Podemos dizer que é basicamente um if/else disfarçado

É importante ressaltar que dentro do let, temos acesso a própria variável "textor" através do it.

Exceções

No Kotlin também dispomos da estrutura de bloco do tipo Try...Catch para o tratamento de exceções em nossos códigos.

Durante o desenvolvimento de nossos aplicativos, podem acontecer problemas durante os nossos códigos, e uma forma de tratar esses problemas é fazendo o uso de exceções.

Um bloco Try é um bloco considerado protegido, porque caso ocorra algum tipo de problema em um dos comandos que existam dentro daquele bloco, o compilador não irá acusar um erro e fechar nosso aplicativo.

Mas sim, desviar a execução do código para o bloco Catch, para que o problema seja tratado e que seja feita algum tipo de correção ali dentro.

Nesse caso, podemos dizer que o nosso sistema ainda continua funcionando normalmente mesmo depois de um erro gerado.

Observe abaixo um exemplo de um código que dará um erro de compilação, mas que será tratado pelo bloco catch:

//Forma de usar o Try Catch
val s: String? = null

try{//Tenta executar o comando sem erros, se der erros o catch é executado
println(s!!.length)//Isso vai dar erro e vai executar o bloco do catch
}catch (e: NullPointerException){//Variável temporaria que captura exceções do tipo NullPointer
println("Variável Nula")
}

println("Fim.")//O compilador ainda executará esta linha, isso é uma prova que mesmo após o erro, o sistema continua funcionando

Podemos ter mais um tipo de catch declarado no Try, mas somente um único try:

 try{
val a = 10/0//Esse código irá gerar um erro, devido a divisão por ZERO.
}catch(e: NullPointerException){
println("Variável Nula")
}catch(e: ArithmeticException){
println("Impossível dividir por zero...")//Como é um erro matemático, esse catch será executado.
}

Existe uma palavra chamada 'Finally' que pode ser usado com try...catch, ele é executado sempre (independente se der erro ou não):

try{
val a = 10/0
}catch(e: Exception){
println("Exceção Genérica que contém todos os tipos")
}catch(e: NullPointerException){
println("Variável Nula")
}catch(e: ArithmeticException){
println("Impossível dividir por zero...")
}finally {
println("Final dos blocos Try...Catch")
}

Tenha em mente que na expressão acima, os erros de 'NullPointerException' e 'ArithmeticException' nunca irão ser executados, mesmo que o tipo de exceção seja esta.

Isso acontece, pois temos acima desses dois, o erro de 'Exception' que é a forma mais genérica, e assim, o Kotlin executa ele, ignorando os demais.

O que poderíamos fazer é trocar a ordem:

try{
val a = 10/0
}catch(e: NullPointerException){
println("Variável Nula")
}catch(e: ArithmeticException) {
println("Impossível dividir por zero...")
}catch(e: Exception){
println("Exceção Genérica que contém todos os tipos")
}finally {
println("Final dos blocos Try...Catch")
}

Você pode consultar os diversos tipos de exceções clicando neste link: https://kotlinlang.org/docs/exceptions.html

Conclusão

Neste conteúdo você aprendeu um pouco mais sobre o uso dos nulos (NullSafety) e os blocos try...catch...finally (Exceções).

Até o próximo conteúdo 😁