DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on

Kotlin Koans BR: Tipos anuláveis

🔗 Tarefa

Reescreva o código a seguir para que ele tenha apenas uma expressão if:

Java

package main;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class MinhaClasse {

    public void sendMessageToClient(
        @Nullable Client client,
        @Nullable String message,
        @NotNull Mailer mailer
    ) {
        if (client == null || message == null) return;

        PersonalInfo personalInfo = client.getPersonalInfo();
        if (personalInfo == null) return;

        String email = personalInfo.getEmail();
        if (email == null) return;

        mailer.sendMessage(email, message);
    }
}
Enter fullscreen mode Exit fullscreen mode

JavaScript

function sendMessageToClient(client, message, mailer) {
    if (client === null || message === null) return;

    const personalInfo = client.getPersonalInfo();
    if (personalInfo === null) return;

    const email = personalInfo.getEmail();
    if (email === null) return;

    mailer.sendMessage(email, message);
}
Enter fullscreen mode Exit fullscreen mode

TypeScript

interface Client {
    getPersonalInfo: () => PersonalInfo | null;
}

interface PersonalInfo {
    getEmail: () => string | null;
}

interface Mailer {
    sendMessage: (email: string, message: string) => void;
}

function sendMessageToClient(client: Client | null, message: string | null, mailer: Mailer): void {
    if (client === null || message === null) return;

    const personalInfo: PersonalInfo | null = client.getPersonalInfo();
    if (personalInfo === null) return;

    const email: string | null = personalInfo.getEmail();
    if (email === null) return;

    mailer.sendMessage(email, message);
}
Enter fullscreen mode Exit fullscreen mode

Python

def send_message_to_client(client, message, mailer):
    if client is None or message is None:
        return

    personal_info = client.get_personal_info()
    if personal_info is None:
        return

    email = personal_info.get_email()
    if email is None:
        return

    mailer.send_message(email, message)
Enter fullscreen mode Exit fullscreen mode

Swift

func sendMessageToClient(client: Client?, message: String?, mailer: Mailer) {
    guard let client = client, let message = message else { return }

    guard let personalInfo = client.getPersonalInfo() else { return }

    guard let email = personalInfo.getEmail() else { return }

    mailer.sendMessage(email: email, message: message)
}
Enter fullscreen mode Exit fullscreen mode

PHP

getPersonalInfo();
    if($personalInfo === null) {
        return;
    }

    $email = $personalInfo->getEmail();
    if($email === null) {
        return;
    }

    $mailer->sendMessage($email, $message);
}
?>
Enter fullscreen mode Exit fullscreen mode

Dart

void sendMessageToClient(Client client, String message, Mailer mailer) {
  if (client == null || message == null) return;

  PersonalInfo personalInfo = client.getPersonalInfo();
  if (personalInfo == null) return;

  String email = personalInfo.getEmail();
  if (email == null) return;

  mailer.sendMessage(email, message);
}
Enter fullscreen mode Exit fullscreen mode

Go

package main

func sendMessageToClient(client *Client, message string, mailer *Mailer) {
    if client == nil || message == "" {
        return
    }

    personalInfo := client.getPersonalInfo()
    if personalInfo == nil {
        return
    }

    email := personalInfo.getEmail()
    if email == "" {
        return
    }

    mailer.sendMessage(email, message)
}

type Client struct {
    personalInfo *PersonalInfo
}

func (c *Client) getPersonalInfo() *PersonalInfo {
    return c.personalInfo
}

type PersonalInfo struct {
    email string
}

func (pi *PersonalInfo) getEmail() string {
    return pi.email
}

type Mailer struct{}

func (m *Mailer) sendMessage(email string, message string) {
    // lógica de envio de mensagem
}

Enter fullscreen mode Exit fullscreen mode

C#

public void SendMessageToClient(
    Client client,
    string message,
    Mailer mailer
){
    if(client==null || message==null) return;

    PersonalInfo personalInfo=client.GetPersonalInfo();
    if(personalInfo==null) return;

    string email=personalInfo.Email; 
    if(email==null) return;

    mailer.SendMessage(email, message);
}
Enter fullscreen mode Exit fullscreen mode

Caso de uso

No mundo da programação, é comum encontrar situações em que variáveis não possuem um valor atribuído, sendo identificadas como "nulas".

No Kotlin, o tipo nulo assegura que variáveis tenham ou não um valor, proporcionando uma camada extra de segurança ao código. Assim, quando um valor pode ser nulo, isso é claramente indicado.

Variáveis que podem ser nulas

Sempre que uma variável pode ser nula, a linguagem permite adicionar um ? logo após o tipo da variável:

Para acessar os atributos desse tipo nulo de forma segura, podemos utilizar a operação ?.

val textoNulo: String? = null
val tamanho: Int? = textoNulo?.length
println(tamanho == null) //Saída: true
Enter fullscreen mode Exit fullscreen mode

O operador Elvis ?:

O operador Elvis entrega um valor substituto ou padrão quando o valor à sua esquerda é null.

Note que com o operador Elvis, podemos remover o tipo nulo do Int:

val textoNulo: String? = null
val tamanho: Int = textoNulo?.length ?: 0
println(tamanho == null) //Saída: false
Enter fullscreen mode Exit fullscreen mode

Inclinando a cabeça para o lado esquerdo, nota-se que o símbolo ?: lembra os olhos e a mecha de cabelo típica de Elvis Presley.

Burlando a Nulabilidade em Kotlin

Embora Kotlin trate nulabilidade de maneira segura, existem situações que exigem um contorno dessa proteção.

Operador !!

Ao ter certeza de que uma variável nullable não está nula, é possível utilizar o operador !! para tratá-la como se não fosse nula.

❗❗️No entanto, se a variável for realmente nula, o programa lançará uma NullPointerException.

val nome: String? = null
val tamanho = nome!!.length  // NullPointerException
Enter fullscreen mode Exit fullscreen mode

Utilizando lateinit

No Kotlin, as variáveis devem ser inicializadas com um valor.

É comum utilizar o tipo nulo para representar o estado de uma variável não inicializada.

Para esses casos, podemos utilizar o modificador lateinit, que informa ao compilador que essa variável será inicializada antes de seu acesso, evitando a necessidade de torná-la nullable.

No entanto, se tentarem acessá-la antes da sua inicialização, ocorrerá uma UninitializedPropertyAccessException.

lateinit var nome: String
println(nome) // UninitializedPropertyAccessException
nome = "Kotlin"
Enter fullscreen mode Exit fullscreen mode

Boas práticas

  1. Minimizar o uso: se há certeza de que uma variável nunca será nula, é aconselhável defini-la como não anulável. Isso simplifica o código e minimiza possíveis erros.
  2. Prudência no uso do Elvis ?: é crucial. o valor padrão precisa ser apropriado para o contexto da expressão.
  3. Evitar burlar os tipos nulos: ao invés de forçar uma variável a ser tratada como não nula com !!, é benéfico optar pelo ?. e modelar seu código com uma tipagem segura.
  4. Cuidado ao utilizar lateinit: seu uso imprudente pode ser arriscado. É vital garantir a inicialização da variável antes de acessá-la, além de poder violar princípios de imutabilidade.
  5. Realize testes rigorosos: quando criar testes, é fundamental abordar cenários onde variáveis possam estar nulas.

Analogia

Uma caixa de correio pode ter ou não encomendas em seu interior, semelhante a uma variável no Kotlin. Em certos momentos, ela pode conter uma encomenda (um valor), enquanto em outros, está vazia (nula).

Assim como alguém checa a caixa antes de retirar uma encomenda, no Kotlin o ? indica que essa "caixa" pode estar vazia.

val encomenda: Encomenda? = checarCaixaDeCorreio()
val remetente: String? = encomenda?.remetente

if (remetente == null || encomenda == null) {
    println("Ainda não chegou")
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)