Skip to content

Object and companion objects

Object and Companion Objects Deep Dive

Overview

Kotlin's object keyword covers several patterns:

  • singleton declarations
  • anonymous object expressions
  • companion objects

Interviewers often test whether you understand both the syntax and the runtime differences.


Core Concepts

Object declaration

object Logger {
    fun log(message: String) = println(message)
}
  • named singleton
  • lazily initialized on first access
  • useful for stateless shared utilities

Object expression

val listener = object : Runnable {
    override fun run() = println("run")
}
  • anonymous object
  • created immediately
  • often used for local callbacks

Companion object

class User {
    companion object {
        const val TABLE = "users"
    }
}
  • tied to a class
  • often used for factories/constants
  • Kotlin alternative to many Java static use cases

Internal Implementation

Companion objects are still real objects. They are not magical static blocks.

That means they can:

  • implement interfaces
  • hold state
  • contain functions
  • be referenced as objects
class FactoryHolder {
    companion object Factory : Comparator<Int> {
        override fun compare(a: Int, b: Int): Int = a - b
    }
}

JVM / Compiler Behavior

On JVM, Kotlin often generates a companion instance field plus methods.

With annotations like @JvmStatic, interop can become more Java-friendly.

Interview nuance:

  • companion objects are object instances
  • Java statics are class-level members
  • the call style may look similar, but the compiled model differs

Code Examples

Factory pattern

class User private constructor(val id: Long) {
    companion object {
        fun fromId(id: Long): User = User(id)
    }
}

Anonymous object for one-off behavior

fun makeRunnable(): Runnable {
    return object : Runnable {
        override fun run() = println("working")
    }
}

Common Interview Questions

  • Q: Is a companion object the same as static? A: Tie Kotlin language features to production outcomes: safety, readability, testability, and runtime or allocation tradeoffs when relevant.
  • Q: When is an object declaration initialized? A: Tie Kotlin language features to production outcomes: safety, readability, testability, and runtime or allocation tradeoffs when relevant.
  • Q: What is lost when returning an anonymous object from a public API? A: Tie Kotlin language features to production outcomes: safety, readability, testability, and runtime or allocation tradeoffs when relevant.

Production Considerations

Use object declarations for:

  • stateless helpers
  • shared formatters
  • singleton configuration readers

Avoid hidden global mutable state unless truly necessary.


Performance Insights

The main concern is usually not speed but initialization and hidden state. Singleton misuse can quietly turn into global coupling.


Senior-Level Insights

Strong answers emphasize:

  • object forms model different intent
  • companion objects are still objects
  • singletons simplify access but can hurt testability if overused