O que é var em Kotlin?
A palavra-chave var em Kotlin serve para declarar variáveis mutáveis, ou seja, variáveis cujo valor pode ser alterado depois da atribuição inicial. Se você precisa de um valor que vai mudar ao longo da execução do programa, var é o caminho.
Enquanto val trava o valor, var te dá liberdade para reatribuir quantas vezes quiser — desde que o novo valor seja do mesmo tipo.
Quando usar var?
A regra de ouro é simples: só use var quando realmente precisar alterar o valor. Contadores, acumuladores, estados que mudam durante a execução — esses são casos clássicos onde var faz sentido.
Não saia usando var em tudo só por costume. O compilador do Kotlin inclusive sugere trocar pra val quando detecta que a variável nunca é reatribuída.
Exemplo prático
fun main() {
var contador = 0
for (i in 1..5) {
contador += i
}
println("Soma total: $contador") // Soma total: 15
var status = "pendente"
println("Status: $status")
status = "concluído"
println("Status: $status")
}
No exemplo acima, tanto contador quanto status precisam mudar de valor, então faz todo sentido usar var.
Tipo é fixo, mesmo com var
Um detalhe que pega muita gente: mesmo usando var, o tipo da variável não muda. Se você declarou como Int, vai ser Int pra sempre.
var idade = 25
// idade = "vinte e cinco" // Erro! Não pode mudar o tipo
var mensagem = "Olá"
mensagem = "Tchau" // Tudo certo, continua sendo String
Kotlin é uma linguagem de tipagem estática, então o tipo é definido na declaração e ponto final. O var muda o valor, não o tipo.
var vs val
Na dúvida, comece sempre com val. Se o compilador reclamar que você tá tentando reatribuir, aí sim troque pra var. Essa abordagem deixa o código mais seguro e fácil de manter.
var com propriedades customizadas
Uma funcionalidade poderosa do Kotlin é a possibilidade de definir getters e setters customizados para propriedades var. Isso permite adicionar válidação ou lógica extra na atribuição.
class ContaBancaria(saldoInicial: Double) {
var saldo: Double = saldoInicial
private set // Só a própria classe pode alterar
var limite: Double = 1000.0
set(value) {
if (value >= 0) {
field = value
} else {
println("Limite nao pode ser negativo")
}
}
fun depositar(valor: Double) {
if (valor > 0) saldo += valor
}
fun sacar(valor: Double): Boolean {
return if (valor <= saldo + limite) {
saldo -= valor
true
} else {
false
}
}
}
fun main() {
val conta = ContaBancaria(500.0)
conta.depositar(200.0)
println("Saldo: ${conta.saldo}") // Saldo: 700.0
conta.limite = -100.0 // Limite nao pode ser negativo
println("Limite: ${conta.limite}") // Limite: 1000.0
}
var com lateinit
Quando você sabe que uma variável será inicializada antes de ser usada, mas não pode atribuir o valor no momento da declaração, lateinit var é a solução. Isso é muito comum em frameworks como Android e Spring.
class PerfilUsuario {
lateinit var nome: String
lateinit var email: String
fun inicializar(nome: String, email: String) {
this.nome = nome
this.email = email
}
fun exibir() {
if (::nome.isInitialized) {
println("Nome: $nome, Email: $email")
} else {
println("Perfil nao inicializado")
}
}
}
fun main() {
val perfil = PerfilUsuario()
perfil.exibir() // Perfil nao inicializado
perfil.inicializar("Mariana", "mariana@email.com")
perfil.exibir() // Nome: Mariana, Email: mariana@email.com
}
Casos de Uso no Mundo Real
- Estado de interface no Android: Em ViewModels e Activities, variáveis como
isLoading,errorMessageecurrentPageprecisam mudar conforme o usuário interage. Usarvar(geralmente viaMutableStateFlowouMutableLiveData) é essencial para refletir essas mudanças na tela. - Contadores e acumuladores: Em processamento de dados, relatórios ou análises, contadores de iteração, somas parciais e valores acumulados precisam ser reatribuídos a cada passo. São casos onde
varé indispensável. - Configuração de builders: Padrões como o Builder Pattern dependem de propriedades mutáveis que são configuradas passo a passo antes de construir o objeto final. Cada chamada de método no builder altera uma propriedade
varinterna. - Loops com condição de parada: Variáveis de controle como flags booleanas (
var encontrado = false) ou variáveis de resultado que são preenchidas durante a iteração são cenários legítimos paravar.
Boas Práticas
- Minimize o escopo de variáveis
var: Declare a variável o mais perto possível de onde ela é usada. Quanto menor o escopo, menor o risco de alterações inesperadas e mais fácil de rastrear mudanças. - Considere alternativas funcionais: Muitas vezes, um
varcom loop pode ser substituído por operações funcionais comofold,reduce,mapousumOf. Essas alternativas eliminam a necessidade de mutabilidade e tornam o código mais expressivo. - Use
private setpara controlar acesso: Se uma propriedadevarde uma classe deve ser lida externamente mas modificada apenas internamente, declare o setter comoprivate set. Isso preserva o encapsulamento. - Evite
varem data classes: Propriedadesvaremdata classpodem causar problemas quando o objeto é usado como chave em mapas ou sets, pois ohashCodemuda com a reatribuição. Prefiravale usecopy()para criar versões modificadas. - Prefira
MutableStateFlowavarobservável: Em projetos com Kotlin coroutines, usarMutableStateFlowem vez de umvarsimples oferece reatividade e thread-safety nativos, sem necessidade de sincronização manual.
Erros Comuns
- Usar
varpor padrão: O erro mais comum de quem vem de outras linguagens é declarar tudo comovar. Em Kotlin, a abordagem correta é o inverso: comece comvale só mude paravarquando realmente precisar reatribuir o valor. - Acessar
varde múltiplas threads sem sincronização: Variáveisvarnão são thread-safe. Se duas threads modificam a mesma variável ao mesmo tempo, você terá condições de corrida. UseAtomicInteger,MutexouMutableStateFlowpara cenários concorrentes. - Confundir reatribuição com mutação:
var lista = listOf(1, 2, 3)permite reatribuir a referência (lista = listOf(4, 5)), mas não permitelista.add()porquelistOfretorna uma lista imutável. Jával lista = mutableListOf(1, 2, 3)permitelista.add()mas não permite reatribuir a referência. São conceitos diferentes. - Usar
lateinit varcom tipos primitivos:lateinitnão pode ser usado comInt,Double,Booleane outros tipos primitivos. Para esses casos, inicialize com um valor padrão ou useDelegates.notNull<Int>(). - Não verificar
isInitializedantes de acessarlateinit: Acessar uma variávellateinitantes de inicializá-la causaUninitializedPropertyAccessException. Sempre verifique com::propriedade.isInitializedquando houver dúvida sobre a inicialização.
Perguntas Frequentes
Posso mudar o tipo de uma variável var? Não. Kotlin é uma linguagem com tipagem estática. O tipo é definido na primeira atribuição (ou explicitamente na declaração) e não pode ser alterado depois. Se você precisa armazenar tipos diferentes, considere usar Any como tipo, mas isso raramente é recomendado.
var é mais lento que val? Na prática, a diferença de performance é irrelevante. No entanto, val permite que o compilador faça certas otimizações (como inlining) que não são possíveis com var, já que o valor pode mudar. A escolha entre var e val deve ser baseada em semântica, não em performance.
Quando devo usar var em vez de uma coleção mutável com val? Se você precisa substituir a coleção inteira, use var com uma coleção imutável (var lista = listOf(...)). Se precisa apenas adicionar ou remover itens, use val com uma coleção mutável (val lista = mutableListOf(...)). Evite combinar var com coleção mutável, pois isso gera ambiguidade sobre como a modificação deve ocorrer.
É possível observar mudanças em uma variável var? Sim, usando Delegates.observable. Isso permite executar código toda vez que o valor da variável é alterado, o que é útil para logging, atualização de UI ou válidação.
Termos Relacionados
- val — A contraparte imutável de
var, usada para variáveis somente leitura. - Data Class — Classes de dados onde geralmente se prefere
valavarnas propriedades. - Null Safety — Sistema de tipos nulos do Kotlin que se aplica tanto a
varquanto aval. - when — Expressão condicional que pode retornar valores para atribuição a variáveis
varouval.