O que é Companion Object em Kotlin?
Resposta rápida: o companion object é um objeto declarado dentro de uma classe cujos membros você acessa direto pelo nome da classe — Configuracao.VERSAO — sem criar instância. É o substituto Kotlin para os membros static do Java: como Kotlin não tem a palavra-chave static, constantes, factory methods e utilitários da classe vivem no companion object. Ele é instanciado uma única vez (lazy, no primeiro acesso) e pode ter nome, implementar interfaces e receber extension functions.
O companion object é um objeto especial declarado dentro de uma classe que funciona como o equivalente aos membros estáticos do Java. Com ele, você pode acessar propriedades e funções diretamente pelo nome da classe, sem precisar criar uma instância.
Kotlin não tem a palavra-chave static. Em vez disso, usa companion objects – uma abordagem mais flexível e orientada a objetos.
Pense no companion object como a “recepção” de um prédio comercial. Você não precisa entrar em nenhuma sala específica (instância) para obter informações gerais – basta perguntar na recepção. Da mesma forma, o companion object guarda informações e funcionalidades que pertencem à classe como um todo, não a uma instância individual.
Sintaxe básica
class Configuração {
companion object {
const val VERSAO = "2.1.0"
val AMBIENTE = "producao"
fun info(): String {
return "Versão $VERSAO - Ambiente: $AMBIENTE"
}
}
}
fun main() {
println(Configuração.VERSAO) // 2.1.0
println(Configuração.info()) // Versão 2.1.0 - Ambiente: producao
}
Repare que você acessa versão e info() direto pela classe, sem instanciar nada.
Factory methods
Um dos usos mais comuns do companion object é para criar factory methods – funções que constroem objetos de formas alternativas:
data class Usuario(val nome: String, val email: String) {
companion object {
fun criarAdmin(nome: String): Usuario {
return Usuario(nome, "$nome@admin.com.br")
}
fun criarConvidado(): Usuario {
return Usuario("Convidado", "[email protected]")
}
}
}
fun main() {
val admin = Usuario.criarAdmin("karina")
val guest = Usuario.criarConvidado()
println(admin) // Usuario(nome=karina, [email protected])
println(guest) // Usuario(nome=Convidado, [email protected])
}
Companion object com nome e interface
O companion object pode ter um nome e até implementar interfaces:
interface Serializavel {
fun fromJson(json: String): Any
}
class Produto(val nome: String) {
companion object Fabrica : Serializavel {
override fun fromJson(json: String): Produto {
return Produto(json) // simplificado
}
}
}
Quando usar?
Use companion object para constantes, factory methods, e qualquer funcionalidade que pertença à classe como um todo e não a uma instância específica. É o jeito Kotlin de lidar com o que seria static em Java.
Companion object, object, static ou constante top-level?
| Situação | Alternativa recomendada | Por quê |
|---|---|---|
Constante ligada à classe (Log.NIVEL_DEBUG) | companion object com const val | fica no namespace da classe, sem poluir o arquivo |
Constante genérica do arquivo (const val PI) | const val de nível superior | menos indireção, acesso direto |
Singleton independente (Database, ApiClient) | object de nível superior | não precisa de classe hospedeira |
Factory method da classe (Usuario.fromCsv) | companion object com fun | chamada idiomática Usuario.fromCsv(...) |
| Campo/método estático chamado de Java | companion object + @JvmStatic/@JvmField | interop sem Companion. no meio do caminho |
| Vários agrupamentos utilitários | múltiplos object nomeados | cada classe aceita apenas um companion object |
Companion object com extension functions
Você pode adicionar extension functions ao companion object de uma classe, o que permite estender funcionalidades sem modificar o código original:
class Validador {
companion object
}
fun Validador.Companion.cpf(valor: String): Boolean {
return valor.replace("[^0-9]".toRegex(), "").length == 11
}
fun Validador.Companion.email(valor: String): Boolean {
return valor.contains("@") && valor.contains(".")
}
fun main() {
println(Validador.cpf("123.456.789-00")) // true
println(Validador.email("[email protected]")) // true
println(Validador.email("invalido")) // false
}
Companion object como singleton de configuração
Um padrão muito utilizado é usar companion object para gerenciar configurações ou registros de uma classe:
class Logger private constructor(val nome: String) {
companion object {
private val instancias = mutableMapOf<String, Logger>()
fun obter(nome: String): Logger {
return instancias.getOrPut(nome) {
println("Criando logger para: $nome")
Logger(nome)
}
}
}
fun info(mensagem: String) {
println("[$nome] INFO: $mensagem")
}
}
fun main() {
val logA = Logger.obter("App")
val logB = Logger.obter("App") // reutiliza a instância existente
val logC = Logger.obter("Database")
logA.info("Aplicação iniciada")
logC.info("Conexão estabelecida")
println(logA === logB) // true -- mesma instância
}
Casos de Uso no Mundo Real
- Constantes de configuração: agrupar URLs de API, chaves de preferências, códigos de erro e outras constantes que pertencem ao contexto da classe.
- Factory methods com válidação: criar instâncias com regras de negócio, como
Usuario.fromCsv(linha)ouPedido.criarRascunho(). - Interoperabilidade com Java: usar a anotação
@JvmStaticem métodos do companion object para que o código Java possa chamar como métodos estáticos reais. - Serialização e desserialização: implementar funções
fromJson()efromMap()no companion object para converter dados externos em objetos Kotlin. - Registros e caches: manter um mapa de instâncias para evitar criação duplicada, como no padrão mostrado acima.
Boas Práticas
- Use
const valpara constantes primitivas: dentro do companion object, declare constantes de tipos primitivos e String comconst valpara garantir que sejam inlined pelo compilador. - Não coloque lógica de negócio pesada no companion object. Ele deve conter funcionalidades utilitárias relacionadas à classe, não regras complexas.
- Dê um nome ao companion object quando ele implementa uma interface ou quando você quer deixar o propósito claro:
companion object Factory. - Use
@JvmStaticem projetos mistos Kotlin/Java para facilitar a interoperabilidade. - Prefira object de nível superior quando a funcionalidade não está diretamente ligada a uma classe específica.
Erros Comuns
- Confundir companion object com instância da classe: o companion object é compartilhado entre todas as instâncias. Alterar uma propriedade
vardentro dele afeta todas as partes do código que a acessam. - Usar companion object para estado mutável global: isso cria acoplamento e dificulta testes. Se precisa de estado compartilhado, considere injeção de dependência.
- Esquecer que cada classe só pode ter um companion object: se você precisa de múltiplos agrupamentos, use objects regulares nomeados dentro da classe.
- Não usar
constem constantes String: declararvalem vez deconst valpara strings constantes impede a otimização do compilador e gera um campo com getter desnecessário.
Perguntas Frequentes
Qual a diferença entre companion object e object declaration?
O companion object vive dentro de uma classe e permite acessar seus membros pelo nome da classe (como MinhaClasse.função()). Um object declaration é um singleton independente, acessado pelo próprio nome.
Posso ter mais de um companion object por classe?
Não. Cada classe pode ter no máximo um companion object. Se precisar de mais agrupamentos, use object nomeados regulares dentro da classe.
O companion object é inicializado quando? Ele é inicializado na primeira vez que a classe é referenciada, de forma similar ao carregamento de classes no Java. Isso segue o padrão de inicialização lazy.
Como acessar o companion object de Kotlin a partir do Java?
Use MinhaClasse.Companion.método(). Se quiser acessar como método estático, adicione @JvmStatic ao método dentro do companion object.
Qual a diferença entre companion object e static do Java?
O static do Java é um membro da classe sem instância, resolvido em tempo de compilação. O companion object é um objeto real (uma instância singleton da classe interna Companion), inicializado de forma lazy na primeira referência à classe. Por isso ele pode implementar interfaces, ter nome, estender outra classe e receber extension functions — coisas que static não faz. Em código Java, adicione @JvmStatic (para métodos) e @JvmField (para campos val) para gerar membros estáticos de verdade e evitar o prefixo Companion.
Companion object é inicializado quando a classe é carregada?
Não exatamente. Diferente dos inicializadores static do Java, que rodam no carregamento da classe, o companion object é instanciado de forma lazy, apenas quando um membro dele é acessado pela primeira vez.
Próximos Passos
- Classes e objetos em Kotlin — guia completo de classes, construtores,
objecte onde o companion object se encaixa - Extension functions: tutorial — incluindo extensions no companion object
- Object — singleton declarado com
object, o parente direto do companion object - Interface — companion objects que implementam interfaces (padrão factory)
- Kotlin vs Java em 2026 — de onde vem a diferença entre
statice companion object - Glossário de Kotlin — explore todos os termos da linguagem, de
vala coroutines