O que é Nullable em Kotlin?
Em Kotlin, todo tipo é não-nulo por padrão. Se você declara uma variável como String, ela não pode receber null. Para permitir valores nulos, você precisa adicionar ? ao tipo, criando um tipo nullable como String?.
Essa é uma das maiores sacadas do Kotlin: o famoso NullPointerException é praticamente eliminado em tempo de compilação.
Declarando tipos nullable
var nome: String = "Karina"
// nome = null // Erro de compilacao!
var apelido: String? = "Kah"
apelido = null // Tudo certo!
Operador de chamada segura ?.
O operador ?. só executa a operação se o valor não for nulo:
val cidade: String? = "São Paulo"
println(cidade?.length) // 9
val estado: String? = null
println(estado?.length) // null (nao dá erro!)
Operador Elvis ?:
O operador Elvis fornece um valor padrão quando o original é nulo:
val nome: String? = null
val exibicao = nome ?: "Anônimo"
println(exibicao) // Anônimo
Combina muito bem com o operador ?.:
fun tamanhoDoNome(nome: String?): Int {
return nome?.length ?: 0
}
println(tamanhoDoNome("Fernanda")) // 8
println(tamanhoDoNome(null)) // 0
Operador de asserção !!
O !! força a conversão para não-nulo, mas lança exceção se o valor for null:
val valor: String? = "Kotlin"
println(valor!!.length) // 6
val nulo: String? = null
// println(nulo!!.length) // NullPointerException!
Use !! com muito cuidado — só quando você tem certeza absoluta de que o valor não é nulo.
Smart cast com if
O Kotlin faz smart cast automaticamente após verificações de null:
fun imprimir(texto: String?) {
if (texto != null) {
// Aqui o compilador já sabe que texto nao é null
println(texto.uppercase())
}
}
Encadeamento seguro com let
A função let combinada com ?. é uma das formas mais elegantes de trabalhar com nullable:
data class Endereco(val rua: String?, val cidade: String?)
data class Usuario(val nome: String, val endereco: Endereco?)
fun exibirCidade(usuario: Usuario) {
usuario.endereco?.cidade?.let { cidade ->
println("O usuário mora em $cidade")
} ?: println("Cidade nao informada")
}
fun main() {
val usuario1 = Usuario("Ana", Endereco("Rua A", "Curitiba"))
val usuario2 = Usuario("Pedro", null)
exibirCidade(usuario1) // O usuário mora em Curitiba
exibirCidade(usuario2) // Cidade nao informada
}
Coleções e Nullable
Kotlin diferencia entre coleções que podem conter elementos nulos e coleções que podem ser nulas:
// Lista que pode conter elementos nulos
val nomes: List<String?> = listOf("Ana", null, "Carlos", null)
val naoNulos = nomes.filterNotNull()
println(naoNulos) // [Ana, Carlos]
// Lista que pode ser nula
val lista: List<String>? = null
println(lista?.size ?: 0) // 0
// Lista que pode ser nula E conter elementos nulos
val mista: List<String?>? = listOf("Kotlin", null, "Brasil")
val resultado = mista?.filterNotNull()?.joinToString()
println(resultado) // Kotlin, Brasil
Casos de Uso no Mundo Real
- Dados vindos de APIs externas: respostas JSON frequentemente contêm campos opcionais. Usar tipos nullable permite modelar esses campos de forma segura sem arriscar crashes em tempo de execução.
- Formulários de cadastro: campos não obrigatórios como apelido, telefone secundário ou foto de perfil são naturalmente nullable na modelagem do domínio.
- Integração com código Java: bibliotecas Java podem retornar
nullem qualquer lugar. Os tipos nullable do Kotlin servem como camada de proteção nessa integração. - Busca em banco de dados: consultas que podem não encontrar resultados retornam
nullnaturalmente, comofindById()que retornaUsuario?.
Boas Práticas
- Prefira
?.e?:em vez de!!. O operador!!deve ser a exceção, não a regra. - Use
letpara trabalhar com nullable:valor?.let { println(it) }. - Evite tipos nullable quando possível — menos
?, menos problemas. Se um valor sempre vai existir, declare como não-nulo. - Valide valores nullable nas bordas do sistema (entrada de dados, respostas de API) e converta para tipos não-nulos o mais cedo possível.
- Use
requireNotNull()oucheckNotNull()para válidações explícitas com mensagens de erro descritivas, em vez de!!.
Erros Comuns
- Usar
!!em toda parte: isso destrói a segurança contra nulos do Kotlin. Cada!!é um potencialNullPointerExceptionesperando para acontecer. - Ignorar avisos do compilador sobre platform types: quando você usa código Java, o Kotlin mostra tipos como
String!(platform type). Sempre trate esses tipos como nullable para evitar surpresas. - Criar cadeias longas de
?.: encadeamentos comoa?.b?.c?.d?.eindicam que o modelo de dados pode estar mal estruturado. Considere refatorar. - Usar nullable para valores com default: se um campo sempre tem um valor padrão, use esse valor em vez de
null. Por exemplo,val nome: String = ""é melhor queval nome: String? = nullquando string vazia é aceitável. - Esquecer de tratar o caso null: usar apenas
?.sem?:pode resultar emnullsilencioso propagando pelo sistema sem que ninguém perceba.
Perguntas Frequentes
Qual a diferença entre String e String? em Kotlin? String é um tipo não-nulo que nunca pode receber null. String? é o tipo nullable equivalente, que aceita tanto valores de texto quanto null. O compilador trata os dois como tipos diferentes e exige operadores seguros ao trabalhar com String?.
Quando devo usar !! em vez de ?.? Praticamente nunca. O !! deve ser usado apenas quando você tem garantia lógica absoluta de que o valor não é nulo e quer que o programa falhe caso esteja errado. Prefira requireNotNull() que permite incluir uma mensagem de erro explicativa.
O Kotlin elimina completamente o NullPointerException? Não completamente, mas reduz drasticamente. NPEs ainda podem ocorrer ao usar !!, ao interagir com código Java, ou em cenários de concorrência. A diferença é que, no Kotlin, quase todo NPE é uma escolha consciente do desenvolvedor.
Como lidar com nullable em interoperabilidade com Java? Trate todos os tipos vindos de Java como nullable por padrão, a menos que a biblioteca Java use anotações como @NotNull ou @Nullable. Use ?. e ?: para criar uma camada de segurança entre o código Kotlin e o Java.
Termos Relacionados
- Data Class — classes de dados que frequentemente contêm propriedades nullable
- Scope Functions —
let,rune outras funções úteis para trabalhar com nullable - Smart Cast — conversão automática de tipos após verificações de null
- Extension Function — funções de extensão que podem ser declaradas para tipos nullable