O que é object em Kotlin?
A palavra-chave object em Kotlin serve para criar singletons — objetos que existem em uma única instância durante toda a execução do programa. Além disso, object também é usado para criar objetos anônimos, parecidos com as classes anônimas do Java.
Em Kotlin, o padrão Singleton vem de fábrica. Não precisa de construtor privado, campo estático nem nada disso.
Object Declaration (Singleton)
object GerenciadorDeLog {
private val logs = mutableListOf<String>()
fun registrar(mensagem: String) {
logs.add(mensagem)
println("LOG: $mensagem")
}
fun exibirTodos() {
logs.forEach { println(it) }
}
}
fun main() {
GerenciadorDeLog.registrar("Aplicação iniciada")
GerenciadorDeLog.registrar("Usuário fez login")
GerenciadorDeLog.exibirTodos()
}
Você acessa diretamente pelo nome, sem precisar instanciar. O Kotlin garante que só vai existir uma instância de GerenciadorDeLog no programa inteiro.
Object Expression (Objeto anônimo)
Quando você precisa de um objeto que implementa uma interface ou estende uma classe, mas não quer criar uma classe nomeada, usa object expression:
interface Clicavel {
fun aoClicar()
}
fun configurarBotao(acao: Clicavel) {
acao.aoClicar()
}
fun main() {
configurarBotao(object : Clicavel {
override fun aoClicar() {
println("Botão clicado!")
}
})
}
Isso é muito útil em callbacks e listeners, principalmente no desenvolvimento Android.
Companion Object
O companion object é uma forma de criar membros estáticos dentro de uma classe. Ele pertence à classe em si, não a uma instância:
class Conexao private constructor(val url: String) {
companion object {
private const val URL_PADRAO = "https://api.kotlin.dev.br"
fun criar(): Conexao = Conexao(URL_PADRAO)
fun criarCom(url: String): Conexao = Conexao(url)
}
}
fun main() {
val conexao1 = Conexao.criar()
val conexao2 = Conexao.criarCom("https://staging.kotlin.dev.br")
println(conexao1.url) // https://api.kotlin.dev.br
println(conexao2.url) // https://staging.kotlin.dev.br
}
O companion object é o equivalente Kotlin dos métodos estáticos e factory methods do Java. Você pode inclusive dar um nome ao companion object e fazê-lo implementar interfaces.
Object implementando interfaces
Um object pode implementar interfaces, o que é muito útil para criar implementações únicas de estratégias ou comportamentos:
interface Formatador {
fun formatar(valor: Double): String
}
object FormatadorMoeda : Formatador {
override fun formatar(valor: Double): String {
return "R$ %.2f".format(valor)
}
}
object FormatadorPercentual : Formatador {
override fun formatar(valor: Double): String {
return "%.1f%%".format(valor * 100)
}
}
fun exibir(valor: Double, formatador: Formatador) {
println(formatador.formatar(valor))
}
fun main() {
exibir(49.90, FormatadorMoeda) // R$ 49.90
exibir(0.157, FormatadorPercentual) // 15.7%
}
Diferença entre object e class
A principal diferença é que object não tem construtor. Ele é inicializado na primeira vez que é acessado (lazy initialization) e permanece vivo enquanto a aplicação rodar.
| Característica | class | object |
|---|---|---|
| Instâncias | Várias | Uma só |
| Construtor | Sim | Não |
| Herança | Pode herdar e ser herdada | Pode herdar, não pode ser herdada |
Casos de Uso no Mundo Real
- Gerenciadores de configuração: armazenar configurações globais da aplicação em um singleton, como URLs de API, chaves de acesso e parâmetros de ambiente.
- Factory methods com companion object: criar instâncias de classes com construtores privados usando métodos de fábrica no companion object, seguindo o padrão Factory.
- Implementação de listeners no Android: usar object expressions para criar listeners de clique, scroll e outros eventos sem precisar criar classes separadas.
- Repositórios e serviços em aplicações simples: em projetos menores sem injeção de dependência, usar object declarations para repositórios e serviços que precisam ser únicos.
Boas Práticas
- Use
objectpara singletons genuínos que realmente precisam ter uma única instância global. Não abuse do padrão — em projetos maiores, prefira injeção de dependência. - Prefira
companion objectpara factory methods e constantes associadas a uma classe, em vez de funções de nível de pacote. - Dê nomes significativos aos seus companion objects quando eles implementarem interfaces, pois isso melhora a legibilidade ao referenciar externamente.
- Evite armazenar estado mutável em object declarations em ambientes multi-thread sem sincronização adequada, pois singletons são compartilhados entre threads.
- Use object expressions em vez de lambdas quando precisar implementar interfaces com múltiplos métodos.
Erros Comuns
- Confundir object declaration com object expression: object declaration cria um singleton nomeado; object expression cria um objeto anônimo descartável. São usos diferentes da mesma palavra-chave.
- Armazenar estado mutável sem thread safety: como o object é compartilhado globalmente, modificar propriedades mutáveis sem sincronização pode causar race conditions.
- Usar object em vez de injeção de dependência: em projetos com testes automatizados, singletons via
objectdificultam a criação de mocks. Prefira DI frameworks como Koin ou Hilt. - Esquecer que companion object é uma instância real: diferente dos membros estáticos do Java, o companion object é um objeto real que pode implementar interfaces e ser passado como argumento.
- Criar dependências circulares entre objects: como objects são inicializados sob demanda (lazy), dependências circulares podem causar comportamentos inesperados ou deadlocks.
Perguntas Frequentes
Quando o object é inicializado? O object declaration é inicializado de forma lazy, ou seja, na primeira vez que é acessado. A JVM garante que essa inicialização é thread-safe.
Posso usar object com data class? Não. Data classes precisam ter pelo menos um parâmetro no construtor, e objects não têm construtor. Se você precisa de um singleton com dados, use um object declaration com propriedades normais.
Qual a diferença entre object e companion object? O object cria um singleton independente. O companion object existe dentro de uma classe e funciona como os membros estáticos do Java. Cada classe pode ter apenas um companion object.
O object do Kotlin é thread-safe? A inicialização do object é thread-safe graças à JVM. Porém, as operações sobre propriedades mutáveis do object não são automaticamente thread-safe e precisam de sincronização manual.
Termos Relacionados
- Sealed Class — hierarquias fechadas que frequentemente usam
objectpara subtipos sem dados - Companion Object — membros estáticos dentro de classes
- Class — a estrutura básica para criar tipos em Kotlin
- Interface — contratos que objects podem implementar
Use object quando precisar de uma instância única e global, como gerenciadores, configurações ou utilitários.