object, companion object and singletons

Kotlin language Junior Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

An object AnalyticsTracker holds a Context and an HTTP client, and tests can't replace it. What's the better design?

Options
Hint 1 of 1

How would a test swap in a fake?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A Make it a class behind an interface and provide one instance with DI as a singleton

Correct. Same lifetime, but injectable and replaceable in tests.

Why not the others?
  • B Add a reset() function for tests. Tests can still interfere with each other through shared global state.
  • C Make it a companion object. Still a global singleton.
  • D Use lazy inside the object. Changes when it's created, not how it's replaced.

30-second answer

An object declaration defines a class and its single instance at once: a thread-safe, lazily created singleton. A companion object is an object tied to a class, used for factory functions and constants called on the class name. An object expression creates an anonymous object on the spot, like Java's anonymous classes.

Practise saying it · 30 s. No recording, just a timer

Explain object and companion object.

Say your answer out loud, then check which of these points you covered:

  • object is a lazy thread-safe singleton
  • companion for factories and constants
  • @JvmStatic for Java
  • Prefer DI singletons for services

Deep dive

Companion members look static from Kotlin, but they're instance members of the companion; add @JvmStatic for real static methods for Java callers.

Singleton objects that hold state are global state: hard to test and reset between tests.

Prefer dependency injection with a singleton scope over object for services.

data object gives a singleton a readable toString and proper equals.

A factory in a companion Compile-checked · Kotlin 2.4.21
class Money private constructor(val cents: Long) {
    companion object {
        fun ofEuros(euros: Int) = Money(euros * 100L)
    }
}

Common mistakes

  • Using an object as a service locator for everything.
  • Holding a Context in an object and leaking an Activity.
  • Expecting companion members to be static in Java without @JvmStatic.

Senior take & follow-ups

Mention testability; interviewers want to hear why DI singletons beat objects for services.

Follow-up questions

Think of your answer first, then open the hint.

  1. Is object initialisation thread-safe?

    Show hint

    Yes, it's initialised once, on first access, by the class loader.

  2. How do Java callers see a companion function?

    Show hint

    As Foo.Companion.bar() unless you add @JvmStatic.

Suggest a fix if something here is wrong or out of date.