O que é val em Kotlin?
A palavra-chave val em Kotlin serve para declarar variáveis somente leitura, ou seja, uma vez que você atribui um valor a ela, não dá pra mudar depois. É como se fosse uma constante local — você define o valor e pronto, ele fica fixo dali em diante.
Se você já programou em Java, pode pensar no val como o equivalente ao final. A diferença é que em Kotlin, usar val é muito mais natural e incentivado pela própria linguagem.
Por que usar val?
A grande sacada de usar val é tornar o código mais previsível e seguro. Quando você garante que um valor não vai mudar, fica muito mais fácil de entender o que tá acontecendo no programa. Menos bugs, menos dor de cabeça.
Na prática, a recomendação da comunidade Kotlin é: sempre comece com val. Só troque pra var se realmente precisar alterar o valor depois.
Exemplo prático
fun main() {
val nome = "Kotlin Brasil"
val ano = 2026
println("Bem-vindo ao $nome! Estamos em $ano.")
// Isso aqui daria erro de compilacao:
// nome = "Outro nome" // Val cannot be reassigned
}
Perceba que o compilador já infere o tipo automaticamente. O nome é uma String e o ano é um Int, sem precisar declarar isso explicitamente.
val não significa imutável em tudo
Um ponto importante: val garante que a referência não muda, mas o conteúdo do objeto pode mudar. Por exemplo, se você declara uma lista mutável com val, não pode reatribuir a variável, mas pode adicionar itens na lista.
val frutas = mutableListOf("Manga", "Acerola")
frutas.add("Goiaba") // Funciona!
// frutas = mutableListOf("Banana") // Erro! Não pode reatribuir
Essa distinção é fundamental pra quem tá começando com Kotlin. Usar val sempre que possível é uma prática que vai deixar seu código mais limpo e confiável.
val com tipos explícitos e inicialização tardia
Em algumas situações, você pode querer declarar o tipo explicitamente ou inicializar o val em momentos diferentes dentro do mesmo bloco.
fun calcularDesconto(preco: Double, porcentagem: Int): String {
val desconto: Double
val mensagem: String
if (porcentagem > 0) {
desconto = preco * porcentagem / 100
mensagem = "Desconto de R$ ${"%.2f".format(desconto)}"
} else {
desconto = 0.0
mensagem = "Sem desconto aplicado"
}
return mensagem
}
fun main() {
println(calcularDesconto(150.0, 10)) // Desconto de R$ 15,00
println(calcularDesconto(80.0, 0)) // Sem desconto aplicado
}
O compilador do Kotlin permite que um val seja atribuído em branches diferentes de um if ou when, desde que ele seja atribuído exatamente uma vez em todos os caminhos possíveis.
val em data classes e propriedades
O val é amplamente usado em data class para criar objetos imutáveis, um padrão muito valorizado em programação funcional e em aplicações concorrentes.
data class Produto(
val nome: String,
val preco: Double,
val categoria: String
)
fun main() {
val notebook = Produto("Notebook", 3500.0, "Eletrônicos")
println(notebook) // Produto(nome=Notebook, preco=3500.0, categoria=Eletrônicos)
// Para "alterar", crie uma cópia com copy()
val comDesconto = notebook.copy(preco = 2990.0)
println(comDesconto) // Produto(nome=Notebook, preco=2990.0, categoria=Eletrônicos)
}
Casos de Uso no Mundo Real
- Configurações de aplicação: Valores como URL base da API, chaves de configuração e timeouts são naturalmente imutáveis. Declará-los com
valgarante que nenhuma parte do código vai alterar esses valores acidentalmente durante a execução. - Resultados de consultas ao banco: Quando você busca dados do banco ou de uma API, o resultado retornado não deve ser modificado. Usar
valpara armazenar esses resultados evita alterações involuntárias que poderiam causar inconsistências. - Injeção de dependências: Em frameworks como Koin e Dagger/Hilt, as dependências injetadas são declaradas com
val. Uma vez injetado, o serviço ou repositório não deve ser substituído durante o ciclo de vida do componente. - Parâmetros de funções em Kotlin: Todos os parâmetros de funções em Kotlin são implicitamente
val. Você não pode reatribuí-los dentro do corpo da função, o que é uma decisão de design da linguagem que evita efeitos colaterais inesperados.
Boas Práticas
- Comece sempre com
val: Essa é a regra número um. Declare tudo comovalprimeiro. Só mude paravarse o compilador indicar que você precisa reatribuir o valor. Essa abordagem é chamada de “immutability by default”. - Combine
valcom coleções imutáveis: UselistOf(),mapOf()esetOf()junto comvalpara garantir imutabilidade total. UsarvalcommutableListOf()protege apenas a referência, não o conteúdo. - Use
valem propriedades de classe: Propriedades declaradas comvalem classes são thread-safe para leitura, pois não podem ser reatribuídas após a construção do objeto. - Prefira
valcom expressõeswheneif: Em Kotlin,ifewhensão expressões que retornam valor. Useval resultado = when { ... }em vez de declarar umvare atribuir dentro de cada branch. - Considere
const valpara constantes de compilação: Quando o valor é uma primitiva ou String conhecida em tempo de compilação, useconst valno companion object para otimizar a performance.
Erros Comuns
- Achar que
valtorna o objeto completamente imutável: Como mostrado acima,valprotege apenas a referência. Se o objeto apontado é mutável (comoMutableList), seu conteúdo interno ainda pode ser alterado. Para imutabilidade total, combinevalcom tipos imutáveis. - Usar
varpor hábito de outras linguagens: Quem vem de Java, JavaScript ou Python tende a usarvarpara tudo. Em Kotlin, isso desperdiça uma das maiores vantagens da linguagem. O IntelliJ IDEA inclusive mostra um aviso quando umvarnunca é reatribuído. - Confundir
valcomconst val:valé avaliado em tempo de execução, enquantoconst valé avaliado em tempo de compilação.const valsó pode ser usado com tipos primitivos e String em objetos ou companion objects. - Não usar
valem loops for: A variável de iteração em umforjá é implicitamenteval. Tentar reatribuí-la dentro do loop causa erro de compilação, o que é o comportamento correto e esperado. - Declarar
valsem inicializar fora de condicionais: Umvaldeve ser inicializado antes de ser usado. Se você declará-lo sem valor e esquecer de atribuir em algum branch do código, o compilador vai apontar o erro.
Perguntas Frequentes
Qual a diferença entre val e const val? val pode receber qualquer valor em tempo de execução, incluindo resultados de funções e objetos complexos. const val é restrito a tipos primitivos e String, precisa ser declarado em companion objects ou top-level, e o valor é embutido diretamente no bytecode em tempo de compilação.
val é a mesma coisa que final em Java? Conceitualmente sim, ambos impedem a reatribuição da variável. A diferença é que em Kotlin, val é a forma padrão e incentivada de declarar variáveis, enquanto final em Java é uma anotação adicional que poucos desenvolvedores usam consistentemente.
Posso usar val com lateinit? Não. lateinit só pode ser usado com var, pois a variável precisa ser atribuída depois da declaração, o que contradiz a natureza somente-leitura do val. Para inicialização tardia com val, use by lazy.
val torna meu código mais lento? Não. Na verdade, o compilador pode fazer otimizações mais agressivas quando sabe que um valor não vai mudar. Além disso, val não gera overhead de runtime — no bytecode, a diferença é simplesmente a ausência de um método setter.
Termos Relacionados
- var — A contraparte mutável de
val, usada quando o valor precisa ser reatribuído. - Data Class — Classes de dados que usam
valpara criar objetos imutáveis. - Null Safety — Sistema de tipos nulos do Kotlin, frequentemente combinado com
valpara variáveis seguras. - when — Expressão que retorna valor e combina perfeitamente com
valpara atribuição condicional.