Companion Object

1 minute read

Companion Object

If you don’t need to tie member functions to the object, consider for companion object. Regular class elements can access the elements of the companion object, but the companion object elements cannot access the regular class elements.

class WithCompanion {
  companion object {
    val i = 3
    fun doubleIt() = i * 2
  }
  fun tripleIt() = i + doubleIt()
}

fun WithCompanion.Companion.quadrupleIt() = doubleIt() + doubleIt()

val wc = WithCompanion()
wc.tripleIt() // 9
WithCompanion.i // 3
WithCompanion.doubleIt() // 6
WithCompanion.quadrupleIt() // 12

As you can see, you can access to member of companion object using class name WithCompanion.i and WithCompanion.doubleIt().

One thing to note is that ONLY one companion object is allowed per class.

Named and Default

class withNamed {
  companion object Named {
    fun s() = "from Named"
  }
}

class withDefault {
  companion object {
    fun s() = "from Default"
  }
}

withNamed.s() // from Named
withNamed.Name.s() // from Named

withDefault.s() // from Default
withDefault.Companion.s() // from Default [Default Name is "Companion"]

One thing to note is that Kotlin assigns the name Companion if you don’t give the companion object a name. And of course, you can still access its elements without using the name.

Single piece of storage

class Computer {
  companion object {
    private var n: Int = 0
  }

  fun increase() = ++n
}

val a = Computer()
val b = Computer()

a.increase() // 1
b.increase() // 2

Even if we have two separate objects of Computer, when we call the function increase() for both a and b, the value of n is sharing the same memory. n is a single piece of storage. Likewise, increase() function shows that you can access the private members of the companion object from the surrounding class.

Factory Method Pattern

A common use for a companion object is controlling object creation – this is the Factory Method pattern. Suppose you like to only allow the creation of Lists of Book objects, and not individual Book objects.

class Book
private constructor (private val id: int) {
  override fun toString(): String = "Book#$id"
  companion object Factory {
    fun create(size: Int) =
      List(size) { Book(it) }
  }
}

Book.create(0) // []
Book.create(3) // [Book#0, Book#1, Book#2]

Firstly, the Book constructor is private, so the only way for you to create is via the create() factory function.

Tags:

Categories:

Updated: