O que e Immutable em Kotlin?
Imutabilidade (immutability) e o principio de que um valor, uma vez criado, nao pode ser alterado. Em Kotlin, a imutabilidade e fortemente encorajada e suportada pela linguagem em varios niveis: variaveis com val, colecoes somente leitura, data classes com propriedades val e value classes.
Codigo imutavel e mais facil de entender, testar e paralelizar. Quando um objeto nao muda, voce nao precisa se preocupar com quem mais pode estar modificando ele ao mesmo tempo.
val vs var: o primeiro nivel
A forma mais basica de imutabilidade em Kotlin e usar val em vez de var:
val nome = "Kotlin" // Imutavel: nao pode ser reatribuido
var idade = 10 // Mutavel: pode ser reatribuido
// nome = "Java" // Erro de compilacao!
idade = 11 // OK
Porem, val garante apenas que a referencia nao muda. O objeto apontado ainda pode ser mutavel:
val lista = mutableListOf(1, 2, 3)
lista.add(4) // OK! A referencia nao mudou, mas o conteudo sim
// lista = mutableListOf(5) // Erro! Nao pode reatribuir val
Colecoes somente leitura
Kotlin distingue entre colecoes somente leitura e mutaveis:
// Somente leitura: nao expoe metodos de mutacao
val nomes: List<String> = listOf("Ana", "Bruno", "Carlos")
// nomes.add("Diana") // Erro de compilacao!
// Mutavel: permite adicionar, remover, alterar
val nomesMutaveis: MutableList<String> = mutableListOf("Ana", "Bruno")
nomesMutaveis.add("Carlos") // OK
A mesma distincao existe para Set/MutableSet e Map/MutableMap:
val mapaLeitura: Map<String, Int> = mapOf("a" to 1, "b" to 2)
val mapaMutavel: MutableMap<String, Int> = mutableMapOf("a" to 1)
mapaMutavel["c"] = 3 // OK
val conjuntoLeitura: Set<Int> = setOf(1, 2, 3)
val conjuntoMutavel: MutableSet<Int> = mutableSetOf(1, 2)
conjuntoMutavel.add(3) // OK
Importante: as colecoes “somente leitura” do Kotlin nao sao verdadeiramente imutaveis na JVM. Elas podem ser convertidas com cast para a versao mutavel. Para imutabilidade real, considere bibliotecas como kotlinx.collections.immutable.
Data classes imutaveis
O padrao recomendado e criar data classes com todas as propriedades val:
data class Usuario(
val id: Long,
val nome: String,
val email: String
)
val usuario = Usuario(1, "Ana", "ana@email.com")
// usuario.nome = "Maria" // Erro! Propriedade val
// Para "modificar", crie uma copia com copy()
val usuarioAtualizado = usuario.copy(nome = "Maria")
println(usuario) // Usuario(id=1, nome=Ana, email=ana@email.com)
println(usuarioAtualizado) // Usuario(id=1, nome=Maria, email=maria@email.com)
O metodo copy() cria uma nova instancia com os campos alterados, sem modificar o original. Isso e a essencia da programacao com dados imutaveis.
Imutabilidade profunda vs rasa
Imutabilidade rasa (shallow) significa que a referencia nao muda, mas objetos internos podem:
data class Departamento(val nome: String, val membros: MutableList<String>)
val dept = Departamento("TI", mutableListOf("Ana", "Bruno"))
dept.membros.add("Carlos") // Compila! A lista interna e mutavel
Para imutabilidade profunda (deep), todos os campos internos tambem devem ser imutaveis:
data class DepartamentoImutavel(val nome: String, val membros: List<String>)
val dept = DepartamentoImutavel("TI", listOf("Ana", "Bruno"))
// dept.membros.add("Carlos") // Erro! List nao tem add
val deptAtualizado = dept.copy(membros = dept.membros + "Carlos")
Colecoes persistentes com kotlinx.collections.immutable
Para imutabilidade garantida e eficiente, use a biblioteca oficial:
import kotlinx.collections.immutable.*
val lista = persistentListOf(1, 2, 3)
val novaLista = lista.add(4) // Retorna nova lista, original inalterada
println(lista) // [1, 2, 3]
println(novaLista) // [1, 2, 3, 4]
val mapa = persistentMapOf("a" to 1, "b" to 2)
val novoMapa = mapa.put("c", 3)
Colecoes persistentes usam estruturas de dados eficientes (como tries) que compartilham memoria entre versoes, evitando copiar tudo a cada modificacao.
Imutabilidade em concorrencia
A principal vantagem pratica da imutabilidade e em codigo concorrente:
// Seguro para acesso concorrente: dados nunca mudam
data class Configuracao(
val maxConexoes: Int,
val timeout: Long,
val habilitarCache: Boolean
)
val config = Configuracao(10, 5000L, true)
// Pode ser lido por qualquer thread sem sincronizacao
launch(Dispatchers.Default) { usarConfig(config) }
launch(Dispatchers.IO) { usarConfig(config) }
Objetos imutaveis nao precisam de locks, mutex ou qualquer mecanismo de sincronizacao. Eles sao inerentemente thread-safe.
Sealed classes e imutabilidade
Sealed classes combinam bem com imutabilidade para modelar estados:
sealed class EstadoDaTela {
object Carregando : EstadoDaTela()
data class Sucesso(val dados: List<String>) : EstadoDaTela()
data class Erro(val mensagem: String) : EstadoDaTela()
}
// Cada estado e imutavel; transicoes criam novos objetos
var estado: EstadoDaTela = EstadoDaTela.Carregando
estado = EstadoDaTela.Sucesso(listOf("item1", "item2"))
Quando usar imutabilidade
- Sempre que possivel: a regra geral em Kotlin e preferir
valsobrevareListsobreMutableList. - Modelos de dados: data classes com propriedades
valsao o padrao ouro para representar dados. - Estado de UI: em Compose e arquiteturas MVI/MVVM, o estado e imutavel e atualizado por substituicao.
- Concorrencia: sempre que dados sao compartilhados entre threads ou coroutines.
- APIs publicas: expor colecoes somente leitura impede que consumidores modifiquem dados internos.
Erros comuns
Assumir que
valsignifica imutavel:valimpede reatribuicao da referencia, mas o objeto pode ser mutavel internamente. Umaval lista: MutableListainda permite modificacoes.Expor MutableList como retorno: retornar uma
MutableListde uma funcao permite que o chamador modifique a colecao interna. RetorneListou use.toList().Usar data class com propriedades var: isso quebra o contrato de imutabilidade e causa bugs sutis com hashCode/equals.
Copiar tudo desnecessariamente: em colecoes grandes, criar copias completas a cada modificacao e ineficiente. Use colecoes persistentes.
Esquecer imutabilidade profunda: ter um
data classcomvalmas com campos que saoMutableListinternamente.
Termos relacionados
- val: palavra-chave que declara uma referencia somente leitura (nao reatribuivel).
- var: palavra-chave que declara uma referencia mutavel.
- Data Class: classe que gera automaticamente
copy(), facilitando a criacao de versoes modificadas de objetos imutaveis. - Value Class: tipo inline que encapsula um valor de forma imutavel sem overhead.
- Sealed Class: hierarquia fechada que modela estados imutaveis de forma segura.
- Collections: a biblioteca padrao distingue entre colecoes somente leitura e mutaveis.
Imutabilidade e um dos pilares do Kotlin idiomatico. A linguagem facilita a escrita de codigo imutavel em todos os niveis, desde variaveis simples ate estruturas de dados complexas. Adotar imutabilidade como padrao torna o codigo mais previsivel, testavel e seguro em ambientes concorrentes.