ProgrammingBackend Developer

What is a companion object in Kotlin, what are its features and limitations? How to use companion objects to create factory methods and static state?

Pass interviews with Hintsage AI assistant

Answer.

A companion object (companion object) in Kotlin is an object declared inside a class with the keyword companion, allowing for the grouping of static methods and properties, similar to static members in Java. Unlike Java static members, every companion object in Kotlin is a full-fledged object, and at the bytecode level, access to its members is compiled as static calls.

Main features and limitations:

  • Default name — if not explicitly defined, the object is named Companion.
  • The companion object can implement any interfaces, which makes it convenient to use, for example, to create factory methods.
  • Inside the companion object, there is access to the private members of the outer class.
  • There can be only one companion object in each class.
  • Members of the companion object can be accessed like static members through the class name, or as members of the object.

Example:

class MyFactory private constructor(val value: Int) { companion object { fun create(x: Int): MyFactory = MyFactory(x) } } val instance = MyFactory.create(10)

Trick Question.

Why is it not recommended to store state (for example, mutable variables) inside a companion object in multithreaded applications?

Answer with example:

If mutable state is placed inside a companion object, it becomes shared among all instances of the class and threads, leading to race conditions without additional synchronization.

class Counter { companion object { var count = 0 fun increment() { count++ } } } Counter.increment() // race condition on simultaneous calls

Examples of real errors due to a lack of knowledge about the subject.


Story

In an Android project, developers placed the environment configuration inside a companion object to manage the debug switch. This led to confusion when different fragments changed the same global value, resulting in unexpected behavior during asynchronous transitions.


Story

In the backend, part of the functionality was moved to a companion object along with a mutable cache. Under high load, errors and inconsistent data state arose due to the lack of synchronization.


Story

A beginner developer tried to implement an interface only in the companion object, thinking that they could then use the class as this interface. As a result, they faced the inability to pass the class instead of the companion object, as the companion is the only object associated with the class, not the class itself.