Como parar de sofrer e encontrar um substituto para Java static em Kotlin
\n\nVocê já reescreveu um bom e velho código Java para Kotlin e de repente se deparou com um muro? Parece tudo simples, lógico, conciso… Mas aí você quer criar um método estático — e Kotlin diz: "Não, amigo, eu não tenho isso". Pânico? Sem motivo. No Stack Overflow, essa pergunta acumulou milhares de visualizações, e não é à toa: o tópico é realmente importante para quem está migrando de Java para Kotlin.
\n\nNeste artigo, não vamos dar apenas uma resposta seca. Vamos analisar o equivalente dos métodos estáticos Java em Kotlin do ponto de vista de desempenho, otimização e facilidade de manutenção. Você aprenderá como não perder velocidade e não pisar em armadilhas. Vamos lá!
\n\nPor que não existe static em Kotlin e para que serve isso?
\n\nVamos direto ao ponto. Em Java, métodos estáticos são métodos que pertencem à classe, e não à sua instância. Eles residem na memória uma única vez, não exigem criação de objeto e são ideais para funções utilitárias, fábricas ou singletons.
\n\n// Java\npublic class MathUtils {\n public static int square(int x) {\n return x * x;\n }\n}\n\n// Uso\nint result = MathUtils.square(5);\n\n\nJá em Kotlin, não existe a palavra-chave static. Por quê? Porque Kotlin incentiva uma abordagem orientada a objetos e funcional simultaneamente. Em vez de static, aqui são usados:
\n\n- \n
- companion object (objeto companheiro) \n
- funções top-level (funções fora da classe) \n
- object (objeto singleton) \n
Cada opção tem suas nuances de desempenho e otimização. Vamos analisar em ordem.
\n\nOpção 1: companion object — um análogo direto do static?
\n\nA resposta mais comum que você encontrará no Stack Overflow é o companion object. Ele se parece com isto:
\n\n// Kotlin\nclass MathUtils {\n companion object {\n fun square(x: Int): Int = x * x\n }\n}\n\n// Uso\nval result = MathUtils.square(5)\n\n\nVisualmente — idêntico ao Java. Mas por baixo dos panos é diferente. companion object é um objeto real, herdeiro de Any. Ele é criado uma vez quando a classe é carregada e armazenado na memória como um objeto comum. Isso significa que:
- \n
- Memória: um pouco mais do que em Java static (devido à sobrecarga do objeto). \n
- Chamada: o compilador Kotlin gera um método estático, mas apenas se você adicionar a anotação
@JvmStatic. \n
@JvmStatic: a mágica da interoperabilidade
\n\nSe você escreve em Kotlin, mas seu código é chamado a partir de Java, sem @JvmStatic você terá uma surpresa desagradável:
// Kotlin\nclass MyClass {\n companion object {\n @JvmStatic\n fun greet() = "Olá!"\n }\n}\n\n// Chamada Java\nMyClass.greet(); // Funciona graças a @JvmStatic\n// Sem @JvmStatic, seria necessário escrever MyClass.Companion.greet()\n\n\nDo ponto de vista de desempenho, @JvmStatic é compilado como um método estático real na JVM. Isso significa que você obtém exatamente a mesma velocidade que em Java. Sem essa anotação — a chamada passa pelo objeto Companion, o que gera um micro-overhead (menos de 1 nanossegundo, mas para sistemas de alta carga isso pode fazer diferença).
Opção 2: Funções top-level — o caminho Kotlin
\n\nKotlin permite declarar funções diretamente no arquivo, fora da classe. Isso é chamado de função top-level. O compilador as transforma em métodos estáticos de uma classe wrapper (nome do arquivo + Kt).
\n\n// Arquivo: MathUtils.kt\nfun square(x: Int): Int = x * x\n\n// Uso\nval result = square(5)\n\n\nPor baixo dos panos, isso é:
\n\n// Java (decompilado)\npublic final class MathUtilsKt {\n public static final int squa