Interview QuestionPractical QuestionFollow-up Questions

Companion Objects in Kotlin

skydovesJaewoong Eum (skydoves)||7 min read

Companion Objects in Kotlin

A companion object in Kotlin is a singleton object declared inside a class using the companion object keyword. Its members belong to the class itself rather than to any instance, serving a role similar to static members in Java but with significantly more capability. Companion objects can implement interfaces, hold state, and participate in polymorphism. By the end of this lesson, you will be able to:

  • Explain how companion objects differ from Java static members.
  • Describe how companion objects are compiled to bytecode.
  • Use companion objects for factory methods, constants, and extension functions.
  • Identify when a companion object is preferable to a top level declaration.
  • Describe how companion objects interact with serialization and dependency injection frameworks.

Companion Objects vs Static Members

Java uses the static keyword to attach fields and methods to a class rather than to instances. Kotlin removes static entirely and replaces it with companion objects. The key difference is that a companion object is a real object. It has a class, it can implement interfaces, and it can be passed around as a value.

interface Factory<T> {
    fun create(): T
}

class User(val name: String) {
    companion object : Factory<User> {
        override fun create(): User = User("default")
    }
}

val factory: Factory<User> = User

In this example, User.Companion implements Factory<User>. You can assign User (which resolves to its companion) to a variable typed as Factory<User>. This is impossible with Java static members because they do not belong to any object.

Bytecode Representation

The Kotlin compiler generates a nested class named Companion inside the enclosing class. Members of the companion object become methods on this nested class, and a static Companion field on the outer class holds the singleton instance. When you annotate a companion object member with @JvmStatic, the compiler also generates a true static method on the outer class for Java interop:

class Config {
    companion object {
        @JvmStatic
        fun defaultTimeout(): Long = 30_000L
    }
}

From Java, Config.defaultTimeout() works as a static call. Without @JvmStatic, Java code must call Config.Companion.defaultTimeout(). Similarly, @JvmField on a companion object property generates a static field directly on the outer class, which is useful for constants consumed by Java code or annotation processors that expect static fields.

This interview continues for subscribers

Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.

Become a Sponsor