DEV Community

Cover image for Situações que NullPointerException podem acontecer no Kotlin
Alex Felipe
Alex Felipe

Posted on

Situações que NullPointerException podem acontecer no Kotlin

Uma das motivações de usar o Kotlin é por ser uma linguagem segura, muito por conta da segurança a referências nulas ou Null Safety.

De uma forma resumida, é a habilidade para evitar que apps escritos em Kotlin lancem a famosa NullPointerException ou NPE. Para isso, a linguagem oferece o suporte a variáveis/parâmetros que podem ou não serem nulos.

Tipos que podem ser nulos (nullables) são identificados com o sufixo ?, como por exemplo String?, Int? etc e isso significa que para chamar qualquer membro de um nullable, seja propriedade ou método, precisamos garantir que ele não é nulo.

Null Safety no Kotlin

O Kotlin oferece uma série de recursos para lidar com nullables, seja por condições com if, chamada segura, operador elvis e outras possibilidades que você pode conferir na página da documentação sobre null safety.

Embora exista diversas opções para escrever códigos que nos protejam do NPE, nem sempre temos essaa garantia!

"Como assim, Alex? Me explica melhor isso ai..."

Pois é, a partir do momento que a nossa aplicação precisa realizar alguma integração, o risco de NPE aumenta!

Reflexão sobre o uso de nullables

O primeiro caso comum é o uso de código escrito em Java no Kotlin, pois não tem um suporte rigoroso como o do Kotlin que nos indique se variáveis, parâmetros ou retornos podem ser nulos.

Em outras palavras, se usarmos um código Java que tenha a possibilidade de retornar null, temos o risco de receber um NPE! Vamos seguir com uma demonstração, criando um modelo para representar um usuário:

public class User {
    private final int id;
    private final String userName;

    User(Integer id, String userName) {
        this.id = id;
        this.userName = userName;
    }

    public Integer getId() {
        return id;
    }

    public String getUserName() {
        return userName;
    }
}
Enter fullscreen mode Exit fullscreen mode

Então, vamos implementar um gerenciador de usuário que permite buscar usuários criados a partir de um id:

import java.util.stream.Stream;

public class UserManager {

    public User findUserById(int userId) {
        var userFound = Stream.of(
                        new User(1, "alex"),
                        new User(2, "felipe"))
                .filter(user -> user.getId() == userId)
                .findFirst();
        return userFound.orElse(null);
    }

}
Enter fullscreen mode Exit fullscreen mode

Observe que o código devolve null caso o usuário não for encontrado. Ao testar esse código em Kotlin com um id que não existe:

println(
    UserManager()
        .findUserById(3)
        .userName
)
Enter fullscreen mode Exit fullscreen mode

Mensagem da stack trace indicando o lançamento de um NullPointerException justificando que não pode chamar getUserName() porque o retorno foi null

Pois é, temos um NPE! E não para por aqui, qualquer código de integração que envolva fazer uma conversão, buscar dados etc, pode resultar em um NPE se não utilizarmos nullables.

Portanto, utilizou um código Java no Kotlin, ou fez uma integração com banco de dados ou API que precisa fazer uma busca, integração etc, considere o uso de nullables mesmo que a interface de comunicação não indique o problema!

Evitando NPE com nullables

Vamos verificar como ficam os códigos utilizando nullables:

val user: User? = UserManager().findUserById(3)
println(
    user?.userName
)
Enter fullscreen mode Exit fullscreen mode

Com apenas essa modificação simples, você diminui a fragilidade da sua aplicação a qualquer mudança que ocorra.

Um caso comum em códigos de integração é o processo de parsing de objetos, como por exemplo, um DTO. A implementação desse tipo de objeto seria:

data class UserDTO(
    val id: Int?,
    val userName: String?
)
Enter fullscreen mode Exit fullscreen mode

E sim, para cada propriedade nova, você deve considerá-la um nullable! E por fim, você pode oferecer métodos de conversão, como por exemplo, um toUser():

data class UserDTO(
    val id: Int?,
    val userName: String?
) {
    fun toUser(): User = User(
        id ?: 0,
        userName ?: ""
    )
}
Enter fullscreen mode Exit fullscreen mode

A regra de preenchimento de valores padrão você pode personalizar da maneira que preferir, até mesmo pode lançar exceptions caso não receba valores necessário, como por exemplo, o id:

data class UserDTO(
    val id: Int?,
    val userName: String?
) {
    fun toUser(): User = User(
        id ?: throw IllegalStateException("usuário deve conter um id"),
        userName ?: ""
    )
}
Enter fullscreen mode Exit fullscreen mode

O que você achou desses pontos em relação a referências nulas no Kotlin? Tem outras dicas ou práticas que aplica no seu dia a dia? Aproveite para compartilhar nos comentários.

Top comments (0)