---
title: "Null Safety em Kotlin: Como Funciona | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/null-safety-kotlin/"
markdown_url: "https://kotlin.dev.br/blog/null-safety-kotlin.MD"
description: "Entenda Null Safety em Kotlin e acabe com NullPointerException. Tutorial completo em português com exemplos práticos e boas práticas."
date: "2026-03-05"
author: "Karina Melo"
---

# Null Safety em Kotlin: Como Funciona | Kotlin Brasil

Entenda Null Safety em Kotlin e acabe com NullPointerException. Tutorial completo em português com exemplos práticos e boas práticas.


Se você já programou em Java, com certeza já tomou aquele susto com `NullPointerException` em produção. Tony Hoare, o criador do conceito de null, chamou isso de "o erro de um bilhão de dólares". Kotlin resolveu esse problema de um jeito elegante: o sistema de tipos diferencia referências que podem ser nulas das que não podem. Vamos entender como.

## O problema com null

Em linguagens como Java, qualquer referência de objeto pode ser `null`. Isso significa que toda vez que você acessa um método ou propriedade, existe o risco de um NPE:

```kotlin
// Em Java, isso compila sem problema, mas estoura em runtime:
// String nome = null;
// int tamanho = nome.length(); // NullPointerException!
```

Kotlin resolve isso em tempo de compilação. Se o código pode causar NPE, ele simplesmente não compila.

## Tipos nullable e non-nullable

Em Kotlin, por padrão, variáveis **não podem ser nulas**:

```kotlin
var nome: String = "Karina"
// nome = null  // ERRO DE COMPILAÇÃO: Null can not be a value of a non-null type String

// Para permitir null, adicione ? ao tipo
var nomeOpcional: String? = "Karina"
nomeOpcional = null  // OK, o tipo permite
```

Essa distinção é feita no sistema de tipos: `String` e `String?` são tipos diferentes. O compilador sabe exatamente quais variáveis podem ser nulas e te obriga a lidar com isso.

## Operador de chamada segura (?.)

O safe call operator é seu melhor amigo quando trabalha com tipos nullable:

```kotlin
val nome: String? = obterNomeDoUsuario()

// Em vez de checar manualmente:
// if (nome != null) { println(nome.length) }

// Use o operador ?.
val tamanho: Int? = nome?.length
println(tamanho) // imprime o tamanho ou null

// Encadeamento seguro
val cidade: String? = usuario?.endereco?.cidade?.uppercase()
```

Se qualquer valor na cadeia for `null`, a expressão inteira retorna `null` sem lançar exceção. Lindo, né?

## Operador Elvis (?:)

Quando você quer um valor padrão caso algo seja `null`, use o operador Elvis:

```kotlin
val nome: String? = null

// Valor padrao simples
val nomeExibido = nome ?: "Usuário Anônimo"
println(nomeExibido) // "Usuário Anônimo"

// Combinando com safe call
val tamanho = nome?.length ?: 0
println(tamanho) // 0

// Elvis com throw ou return
fun buscarUsuario(id: Int): Usuario {
    val usuario = repositorio.buscar(id) ?: throw UsuarioNaoEncontradoException(id)
    return usuario
}

fun processarNome(nome: String?) {
    val nomeValido = nome ?: return  // sai da funcao se for null
    println("Processando: $nomeValido")
}
```

O nome "Elvis" vem do formato `?:` que, se você virar de lado, parece o topete do Elvis Presley. Sério!

## Operador de asserção não-nula (!!)

O operador `!!` converte um tipo nullable para non-nullable, lançando NPE se o valor for `null`:

```kotlin
val nome: String? = "Karina"
val tamanho: Int = nome!!.length // OK, porque nome nao é null

val nomeNulo: String? = null
// val boom = nomeNulo!!.length // NullPointerException!
```

**Atenção**: use `!!` com muita moderação! Cada `!!` no seu código é um NPE em potencial. Se você está usando `!!` com frequência, provavelmente tem algo errado no design do código.

## Smart Casts

Kotlin é esperto: se você já verificou que algo não é null, ele faz smart cast automaticamente:

```kotlin
fun imprimir(texto: String?) {
    if (texto != null) {
        // Aqui o Kotlin já sabe que texto é String (nao-nullable)
        println(texto.length) // sem necessidade de ?. ou !!
        println(texto.uppercase())
    }
}

// Funciona com when tambem
fun descrever(valor: Any?) = when (valor) {
    null -> "Nulo"
    is String -> "String com ${valor.length} caracteres" // smart cast para String
    is Int -> "Inteiro: ${valor * 2}" // smart cast para Int
    else -> "Outro tipo"
}
```

## let, also e outros scope functions com null

A função `let` é muito usada para executar código somente quando o valor não é null:

```kotlin
val email: String? = obterEmailDoUsuario()

// Executa o bloco apenas se email nao for null
email?.let { emailValido ->
    enviarNotificacao(emailValido)
    registrarLog("Email enviado para $emailValido")
}

// Versão mais concisa usando it
email?.let {
    enviarNotificacao(it)
}
```

## Coleções e null safety

Kotlin diferencia coleções que contêm elementos nullable daquelas que não contêm:

```kotlin
val nomes: List<String> = listOf("Ana", "Bruno", "Carla")
val nomesOuNulos: List<String?> = listOf("Ana", null, "Carla")

// filterNotNull remove os nulos e muda o tipo
val apenasNomes: List<String> = nomesOuNulos.filterNotNull()
println(apenasNomes) // [Ana, Carla]

// listOfNotNull já filtra na criacao
val lista = listOfNotNull("Ana", null, "Carla", null)
println(lista) // [Ana, Carla]
```

## Interoperabilidade com Java

Quando você chama código Java a partir de Kotlin, os tipos vêm como **platform types** (indicados por `!`), pois o compilador não sabe se podem ser nulos:

```kotlin
// Retorno de metodo Java: String! (platform type)
val resultado = javaObject.getValor()

// Boas praticas: declare o tipo explicitamente
val resultadoSeguro: String? = javaObject.getValor() // trata como nullable
val resultadoCerto: String = javaObject.getValor()   // assume nao-null (pode dar NPE)
```

A recomendação é: quando chamar código Java, trate o retorno como nullable até ter certeza de que não será null.

## Boas práticas

1. **Prefira tipos non-nullable** sempre que possível
2. **Evite `!!`** — quase sempre existe uma alternativa melhor
3. **Use `?.let`** para operações condicionais
4. **Use Elvis `?:`** para valores padrão
5. **Valide cedo**: converta nullable para non-nullable no início da função
6. **Aproveite `require` e `check`** para válidações:

```kotlin
fun processarPedido(pedidoId: String?, valor: Double) {
    requireNotNull(pedidoId) { "ID do pedido nao pode ser nulo" }
    require(valor > 0) { "Valor deve ser positivo" }

    // Aqui pedidoId já é String (non-nullable)
    println("Processando pedido $pedidoId no valor de R$ $valor")
}
```

## Conclusão

Null Safety é provavelmente o recurso que mais vai poupar suas noites de sono como desenvolvedor. O sistema de tipos de Kotlin te força a pensar em cenários nulos durante a codificação, não em produção. Se segurança de tipos te interessa, <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust leva esse conceito ainda mais longe com ownership e borrowing</a>, eliminando toda uma classe de bugs de memória em tempo de compilação. É uma mudança de mentalidade que, depois que você absorve, faz todo o sentido do mundo.

Bora codar sem medo de NPE!
