Kotlin const vs val: qual a diferença e o que escolher?
\n\nVocê está escrevendo código em Kotlin e de repente se pergunta: "Qual é, afinal, a diferença entre const e val? Ambos não são imutáveis?". Ótima pergunta! Esse ponto confunde até mesmo desenvolvedores experientes que estão migrando do Java ou de outras linguagens. Não é à toa que no Stack Overflow o tópico "What is the difference between 'const' and 'val'?" acumula milhares de visualizações. Hoje vamos entender de uma vez por todas essa questão, organizar a teoria e reforçar tudo com exemplos práticos de código.
Pronto? Vamos lá!
\n\nBreve introdução: o que é val?
\n\nVamos começar pelo mais simples. val (da palavra value — valor) é uma referência imutável. Você pode atribuir um valor a ela uma única vez e nunca mais alterá-lo. Parece familiar? Sim, é análogo ao final em Java ou ao readonly em C#.
val nome: String = "Alice"\nnome = "Bob" // Erro de compilação! Val cannot be reassigned\n\nMas há uma nuance importante: val garante que a referência não mudará, mas não garante a imutabilidade do objeto em si. Se você usar val com uma lista mutável (MutableList), ainda poderá adicionar e remover elementos.
val numeros = mutableListOf(1, 2, 3)\nnumeros.add(4) // Funciona!\n// numeros = mutableListOf(5, 6) // Erro! Não é possível reatribuir a referência\n\nval pode ser inicializado tanto no momento da declaração quanto posteriormente no código (por exemplo, no construtor ou no bloco init). Seu valor é calculado durante a execução do programa (runtime).
E o que é const? O guardião rigoroso do tempo de compilação
\n\nconst é o irmão mais rigoroso do val. Ele também é imutável, mas com um superpoder: o valor deve ser conhecido em tempo de compilação (compile-time). Em outras palavras, é uma constante verdadeira, que o compilador pode substituir diretamente no código, como se fosse embutida.
const tem regras rígidas:
- \n
- Só pode ser declarado no nível do arquivo (fora de uma classe) ou dentro de um
object/companion object. \n - Deve ser de um tipo primitivo ou do tipo
String. \n - Não pode ser o resultado de uma chamada de função ou cálculo — apenas um literal ou outra constante. \n
// Correto\nconst val PI = 3.14159\nconst val NOME_APP = "MeuApp"\n\n// Incorreto\nconst val horaAtual = System.currentTimeMillis() // Erro! Valor não conhecido em tempo de compilação\nconst val nomeUsuario = obterNome() // Erro! Chamada de função não é permitida\n\nPrincipais diferenças: const vs val em tabela
\n\nPara ficar mais claro, aqui está uma tabela comparativa:
\n\n| Critério | \nconst val | \nval | \n
|---|---|---|
| Momento da inicialização | \nEm tempo de compilação | \nEm tempo de execução | \n
| Onde pode ser declarado | \nNível superior, object, companion object | \nEm qualquer lugar (dentro de classe, função, construtor) | \n
| Tipos de dados | \nApenas primitivos e String | \nQualquer tipo (incluindo objetos, listas) | \n
| Desempenho | \nMáximo (inlining) | \nPadrão | \n
| Uso em anotações | \nSim | \nNão | \n
Exemplo prático: anotações e desempenho
\n\nUm dos usos mais comuns