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, errorMessage e currentPage precisam mudar conforme o usuário interage. Usar var (geralmente via MutableStateFlow ou MutableLiveData) é 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 var interna.
  • 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 para var.

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 var com loop pode ser substituído por operações funcionais como fold, reduce, map ou sumOf. Essas alternativas eliminam a necessidade de mutabilidade e tornam o código mais expressivo.
  • Use private set para controlar acesso: Se uma propriedade var de uma classe deve ser lida externamente mas modificada apenas internamente, declare o setter como private set. Isso preserva o encapsulamento.
  • Evite var em data classes: Propriedades var em data class podem causar problemas quando o objeto é usado como chave em mapas ou sets, pois o hashCode muda com a reatribuição. Prefira val e use copy() para criar versões modificadas.
  • Prefira MutableStateFlow a var observável: Em projetos com Kotlin coroutines, usar MutableStateFlow em vez de um var simples oferece reatividade e thread-safety nativos, sem necessidade de sincronização manual.

Erros Comuns

  • Usar var por padrão: O erro mais comum de quem vem de outras linguagens é declarar tudo como var. Em Kotlin, a abordagem correta é o inverso: comece com val e só mude para var quando realmente precisar reatribuir o valor.
  • Acessar var de múltiplas threads sem sincronização: Variáveis var não são thread-safe. Se duas threads modificam a mesma variável ao mesmo tempo, você terá condições de corrida. Use AtomicInteger, Mutex ou MutableStateFlow para 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 permite lista.add() porque listOf retorna uma lista imutável. Já val lista = mutableListOf(1, 2, 3) permite lista.add() mas não permite reatribuir a referência. São conceitos diferentes.
  • Usar lateinit var com tipos primitivos: lateinit não pode ser usado com Int, Double, Boolean e outros tipos primitivos. Para esses casos, inicialize com um valor padrão ou use Delegates.notNull<Int>().
  • Não verificar isInitialized antes de acessar lateinit: Acessar uma variável lateinit antes de inicializá-la causa UninitializedPropertyAccessException. Sempre verifique com ::propriedade.isInitialized quando 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 val a var nas propriedades.
  • Null Safety — Sistema de tipos nulos do Kotlin que se aplica tanto a var quanto a val.
  • when — Expressão condicional que pode retornar valores para atribuição a variáveis var ou val.