---
title: "Null Safety em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/null-safety-tutorial/"
markdown_url: "https://kotlin.dev.br/tutoriais/null-safety-tutorial.MD"
description: "Domine null safety em Kotlin: tipos nullable, safe calls, Elvis operator, non-null assertion, let, smart casts e platform types com exemplos."
date: "2025-06-16"
author: "Karina Melo"
---

# Null Safety em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil

Domine null safety em Kotlin: tipos nullable, safe calls, Elvis operator, non-null assertion, let, smart casts e platform types com exemplos.


Neste tutorial, vamos explorar em profundidade o sistema de null safety do Kotlin, uma das funcionalidades mais importantes e inovadoras da linguagem. NullPointerException (NPE) é historicamente um dos erros mais comuns em Java e outras linguagens. O Kotlin resolve esse problema no nível do sistema de tipos, distinguindo entre referências que podem ser nulas e referências que nunca são nulas. Você vai aprender sobre tipos [nullable](/glossario/nullable/), safe calls, Elvis operator, non-null assertion, funções como `let` e `also`, smart casts e platform types.

## Tipos Nullable e Non-Nullable

Em Kotlin, o sistema de tipos diferencia entre referências que podem conter `null` e referências que não podem. Por padrão, todos os tipos são non-nullable. Para permitir que uma variável contenha `null`, você adiciona `?` ao tipo.

```kotlin
fun main() {
    // Tipos non-nullable: NUNCA podem ser null
    var nome: String = "Kotlin Brasil"
    // nome = null // ERRO DE COMPILAÇÃO: Null can not be a value of a non-null type

    // Tipos nullable: podem ser null
    var sobrenome: String? = "Silva"
    sobrenome = null // OK — tipo permite null

    // Non-nullable é garantido pelo compilador
    println(nome.length) // Seguro: 'nome' nunca é null

    // Nullable precisa de tratamento especial
    // println(sobrenome.length) // ERRO: Only safe (?.) or non-null asserted (!!) calls are allowed
    println(sobrenome?.length) // Seguro: retorna null se sobrenome for null
}
```

Essa distinção no sistema de tipos é verificada em tempo de compilação. Isso significa que a maioria dos NPEs é detectada antes mesmo do programa ser executado, o que representa uma enorme vantagem sobre linguagens como Java.

## Safe Calls (?.)

O operador de safe call `?.` permite acessar propriedades e chamar métodos em referências nullable de forma segura. Se a referência for `null`, a expressão inteira retorna `null` em vez de lançar uma exceção.

```kotlin
fun obterComprimento(texto: String?): Int? {
    return texto?.length
}

fun main() {
    val nome: String? = "Karina Melo"
    val vazio: String? = null

    println(nome?.length)      // 11
    println(vazio?.length)     // null (sem excecao)

    // Encadeamento de safe calls
    val endereco: String? = "Rua das Flores, 123"
    println(endereco?.uppercase()?.take(10)) // RUA DAS FL
    println(null as String?)                  // null (nenhum metodo e chamado)

    // Safe call com atribuição
    val lista: MutableList<String>? = mutableListOf("a", "b")
    lista?.add("c")    // Adiciona normalmente
    println(lista)     // [a, b, c]

    val listaNula: MutableList<String>? = null
    listaNula?.add("x") // Não faz nada, nao lança excecao
    println(listaNula)   // null
}
```

Safe calls são encadeáveis: `a?.b?.c?.d` retorna `null` se qualquer parte da cadeia for `null`. Isso é especialmente útil ao navegar objetos aninhados vindos de APIs ou bancos de dados, onde qualquer nível pode ser ausente.

## Elvis Operator (?:)

O Elvis operator `?:` fornece um valor padrão quando a expressão à esquerda é `null`. O nome vem do fato de que o símbolo `?:` de lado lembra o topete do cantor Elvis Presley.

```kotlin
fun main() {
    val nome: String? = null
    val nomeExibicao = nome ?: "Usuário Anônimo"
    println(nomeExibicao) // Usuário Anônimo

    val email: String? = "karina@kotlin.com"
    val emailExibicao = email ?: "Não informado"
    println(emailExibicao) // karina@kotlin.com (email nao é null)

    // Combinando safe call com Elvis
    val texto: String? = null
    val comprimento = texto?.length ?: 0
    println("Comprimento: $comprimento") // Comprimento: 0

    // Elvis com throw para falhar rápido
    fun buscarUsuario(id: String): String? {
        return if (id == "1") "Karina" else null
    }

    val usuario = buscarUsuario("1") ?: throw IllegalArgumentException("Usuário nao encontrado")
    println(usuario) // Karina

    // Elvis com return para retorno antecipado
    fun processarNome(nome: String?): String {
        val nomeValido = nome ?: return "Nome nao fornecido"
        return "Processando: ${nomeValido.uppercase()}"
    }

    println(processarNome("Maria"))  // Processando: MARIA
    println(processarNome(null))     // Nome nao fornecido
}
```

O padrão `valor?.operação ?: valorPadrao` é extremamente idiomático em Kotlin e substitui boa parte das verificações `if (x != null)` que seriam necessárias em Java. O Elvis operator também pode ser usado com `throw` e `return`, o que permite padrões de falha rápida muito elegantes.

## Non-Null Assertion (!!)

O operador `!!` converte um tipo nullable em non-nullable, lançando `KotlinNullPointerException` se o valor for `null`. Use com extrema cautela — ele é o único mecanismo em Kotlin que pode causar NPE intencionalmente.

```kotlin
fun main() {
    val nome: String? = "Kotlin"
    val nomeDefinitivo: String = nome!! // OK: nome nao é null
    println(nomeDefinitivo.length)      // 6

    // PERIGO: isso lança NullPointerException
    // val nulo: String? = null
    // val boom: String = nulo!! // KotlinNullPointerException!

    // Caso de uso legítimo: quando voce tem certeza da logica
    val numeros = listOf(1, 2, 3, 4, 5)
    val encontrado: Int? = numeros.find { it > 3 }
    // Sabemos que existe pelo menos um numero > 3
    println(encontrado!! * 10) // 40
}
```

A recomendação é evitar `!!` sempre que possível. Na maioria dos casos, safe calls com Elvis operator ou verificações `if` são alternativas melhores. O `!!` deve ser reservado para situações onde você tem absoluta certeza de que o valor não é null e quer que uma falha nessa garantia seja um erro explícito.

## let e also para Verificações de Null

A função de [escopo](/glossario/scope/) `let` é frequentemente combinada com safe call para executar um bloco de código apenas quando o valor não é `null`. Dentro do bloco, o valor é acessado como `it` e já é non-nullable.

```kotlin
fun enviarEmail(destinatario: String) {
    println("Email enviado para: $destinatario")
}

fun main() {
    val email: String? = "usuario@email.com"
    val emailNulo: String? = null

    // let com safe call: executa somente se nao for null
    email?.let {
        println("Email tem ${it.length} caracteres")
        enviarEmail(it)
    }
    // Email tem 19 caracteres
    // Email enviado para: usuario@email.com

    emailNulo?.let {
        println("Isso nunca será impresso")
    }

    // also para efeitos colaterais mantendo a referência original
    val nomeProcessado = email?.also {
        println("Validando email: $it")
    }?.uppercase()
    println(nomeProcessado) // USUARIO@EMAIL.COM

    // Combinando let com Elvis para valor padrao
    val comprimento = email?.let { it.trim().length } ?: 0
    println("Comprimento: $comprimento") // Comprimento: 19

    // Múltiplas verificações com let aninhados
    val nome: String? = "Karina"
    val sobrenome: String? = "Melo"

    nome?.let { n ->
        sobrenome?.let { s ->
            println("Nome completo: $n $s")
        }
    }
    // Nome completo: Karina Melo
}
```

O padrão `variavel?.let { ... }` é mais idiomático do que `if (variavel != null)` quando o objetivo é executar uma ação lateral ou transformar o valor. Para verificações simples, `if` pode ser mais legível.

## Smart Casts

O compilador do Kotlin é inteligente o suficiente para reconhecer verificações de null e automaticamente converter o tipo para non-nullable dentro do bloco correspondente. Isso é chamado de smart cast.

```kotlin
fun processarValor(valor: Any?) {
    // Após verificacao com 'is', o compilador faz smart cast
    if (valor is String) {
        // Aqui 'valor' já é tratado como String (non-nullable)
        println("String de ${valor.length} caracteres: ${valor.uppercase()}")
    }

    // Verificação de null tambem ativa smart cast
    if (valor != null) {
        // Aqui 'valor' é Any (non-nullable)
        println("Valor nao-nulo: $valor (tipo: ${valor::class.simpleName})")
    }
}

fun obterComprimento(texto: String?): Int {
    // Smart cast com return antecipado
    if (texto == null) return 0
    // Daqui em diante, 'texto' é tratado como String (non-nullable)
    return texto.length
}

fun classificar(valor: Any?) {
    when (valor) {
        null -> println("Valor nulo")
        is Int -> println("Inteiro: ${valor * 2}")        // smart cast para Int
        is String -> println("Texto: ${valor.uppercase()}") // smart cast para String
        is List<*> -> println("Lista com ${valor.size} itens") // smart cast para List
        else -> println("Outro tipo: $valor")
    }
}

fun main() {
    processarValor("Kotlin Brasil")
    processarValor(42)
    processarValor(null)

    println(obterComprimento("Olá"))  // 3
    println(obterComprimento(null))   // 0

    classificar("teste")
    classificar(42)
    classificar(listOf(1, 2, 3))
    classificar(null)
}
```

Smart casts funcionam com `if`, [when](/glossario/when/), `&&` e `||`. Eles tornam o código mais limpo, eliminando casts explícitos que seriam necessários em Java. Note que smart casts só funcionam em variáveis locais (`val`) ou propriedades `val` sem getter customizado.

## Platform Types

Quando você usa código Java a partir do Kotlin, os tipos vêm sem informação de nullability — o Java não distingue entre nullable e non-nullable. O Kotlin trata esses tipos como platform types, representados com `!` na documentação (por exemplo, `String!`).

```kotlin
// Exemplo conceitual: chamando codigo Java a partir do Kotlin
// Se tivermos uma classe Java:
// public class JavaUtil {
//     public static String getNome() { return null; } // pode retornar null!
// }

// Em Kotlin, o retorno seria String! (platform type)
// val nome = JavaUtil.getNome() // tipo inferido: String!

// RECOMENDAÇÃO: sempre declare o tipo explicitamente ao lidar com codigo Java
// val nomeSeguro: String? = JavaUtil.getNome() // Seguro: trata como nullable
// val nomePerigoso: String = JavaUtil.getNome() // Perigoso: pode causar NPE

fun main() {
    // Exemplo pratico com collections do Java
    val mapa = java.util.HashMap<String, String>()
    mapa["chave"] = "valor"

    // get() retorna String? em Kotlin (tratamento correto)
    val resultado: String? = mapa["inexistente"]
    println(resultado ?: "Não encontrado") // Não encontrado

    // Sempre trate retornos de APIs Java como nullable
    val propriedade: String? = System.getProperty("minha.prop")
    println(propriedade ?: "Propriedade nao definida")
}
```

A regra de ouro ao trabalhar com interoperabilidade Java é: trate tudo como nullable até ter certeza do contrário. Anote seus tipos explicitamente em vez de depender da inferência quando os valores vêm de código Java.

## Erros Comuns

**Uso excessivo de `!!`.** Cada `!!` é um ponto potencial de NPE. Se você está usando muitos `!!` no seu código, provavelmente está ignorando a proposta do null safety. Prefira safe calls e Elvis operator.

**Não tratar platform types.** Assumir que retornos de APIs Java são non-nullable sem verificação é uma das causas mais frequentes de NPE em projetos Kotlin.

**Smart cast em `var`.** Smart casts não funcionam em propriedades `var` porque outra thread poderia alterar o valor entre a verificação e o uso. Use `let` ou crie uma variável local `val`.

```kotlin
var nome: String? = "Kotlin"
// if (nome != null) println(nome.length) // PODE falhar com var
nome?.let { println(it.length) } // Seguro
```

**Ignorar tipos de coleções nullable.** `List<String>` e `List<String?>` são tipos diferentes. O primeiro garante que nenhum elemento é null; o segundo permite elementos null dentro da lista. `List<String>?` é uma lista que pode ser null, mas seus elementos não.

## Conclusão e Próximos Passos

Neste tutorial, você aprendeu o sistema completo de null safety do Kotlin: tipos nullable, safe calls, Elvis operator, non-null assertion, `let`/`also`, smart casts e platform types. Esses mecanismos trabalham juntos para praticamente eliminar NullPointerExceptions do seu código.

O próximo passo é estudar [collections em Kotlin](/tutoriais/collections-kotlin/), onde você aplicará null safety no contexto de listas, sets e maps. Recomendamos também explorar [coroutines](/glossario/coroutine/) onde null safety é essencial para lidar com resultados assíncronos. Com domínio de null safety, você escreve código Kotlin significativamente mais seguro e confiável do que o equivalente em Java.
