Companion object in Kotlin: replacement for Java static and performance

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why doesn't Kotlin have static, but has companion object? Understanding performance and logic



If you're switching from Java to Kotlin, one of the first questions you'll ask on StackOverflow (and it really has gathered thousands of views) is: "Why do we use companion object instead of Java static? And doesn't it slow down the app?"



Let's be honest: when I first saw the companion object construct, it seemed like the Kotlin team just wanted to make life harder. Why write more code when you could just copy static from Java? But after digging deeper, I realized: it's not a whim, but a well-thought-out architectural decision that makes code cleaner, safer, and... yes, sometimes even faster.



In this article, we won't just translate an answer from SO. We'll break down how companion object works under the hood, compare performance with Java static, and show when to use it and when it's better to choose another path.



What is companion object in simple terms?



In Java, static fields and methods belong to the class, not the instance. In Kotlin, they decided to move away from the static keyword because it violates OOP principles (static cannot be overridden, it is not part of an object). Instead, Kotlin offers companion objects.



Imagine you have a User class. You want to store a constant MAX_AGE and a factory method createGuest(). In Java you would write:



public class User {    public static final int MAX_AGE = 120;    public static User createGuest() {        return new User("guest", "1234");    }}


In Kotlin it looks like this:



class User(private val name: String, private val password: String) {    companion object {        const val MAX_AGE = 120        fun createGuest() = User("guest", "1234")    }}


Note: companion object is not just a "static block". It is a full-fledged object that lives inside your class. It can have interfaces, extensions, and even its own name (if you specify one).



How does companion object work under the hood? (Through the eyes of the JVM)



To understand performance, let's look at the bytecode. When you write a companion object, the Kotlin compiler does the following:



  • Creates an inner static class named Companion (or your custom name).
  • Places all fields and methods from the companion object into this class.
  • If you use the @JvmStatic annotation, the compiler additionally generates static wrapper methods in the class itself.


Example with the @JvmStatic annotation:



class User {    companion object {        @JvmStatic        fun createGuest() = User()    }}


Now from Java you can call User.createGuest() as a regular static method. Without this annotation, you would have to write User.Companion.createGuest().



Performance conclusion: if you work only with Kotlin, there are no additional overheads. If called from Java, there is one extra level of indirection (through the Companion object). But the JVM JIT compiler inlines such calls very well, so the speed difference is negligible.



Performance: companion object vs Java static



Let's dispel the main myth: companion object does NOT slow things down. I ran several micro-benchmarks (on Kotlin 1.9, JVM 17), and here's what I got:



Blogs

Book Recommendations

Scenario Java static