This question was asked by an interviewer recently. I replied that companion object is class level singleton and thus can't have multiple companions. He further asked that we If we have to create singleton class then we can use 'object' keyword to create singleton class. In which situation you should choose companion object over object keyword. This was confusing to me and i couldn't satisfy the interviewer. So, in which situation it is best to use companion object over object keyword.
I was not able to find any reliable answer on stack overflow.
I don't think there's an "objective" answer to this question, but there are some properties of object
s and companion object
s that one can compare to come to some kind of strategy of what to choose in which situation.
Generally speaking, any kind of object
is a singleton declaration in Kotlin, basically a class that instantiates itself once at class loading time and stays alive for the entire lifetime of the program. This is true for both object
and companion object
. More precisely, companion object
is a specialization of object
, so there really is a lot similar for these two. The specific properties of a companion object
that differentiate it from a "regular" object
are:
class
)companion object
per class (as you already correctly pointed out)companion object
an explicit name (in case you don't, it's implicitly called "Companion
")Practical example: You can nest a regular object
and a companion object
in a class:
class Foo {
object Bar {
val barValue = 1
}
companion object Baz { // you can omit the name
val bazValue = 2
}
}
But then, there are differences in how to access Bar
s / Baz
s members:
println(Foo.Bar.barValue) // OK (prints 1)
println(Foo.Baz.bazValue) // OK (prints 2)
println(Foo.barValue) // NOT OK (does not compile)
println(Foo.bazValue) // OK (prints 2)
With all that being said, I would come to the following conclusions regarding usage of object
s vs companion object
s for the singleton pattern:
object
s, including companion object
s, are a valid realization of the singleton patterncompanion object
s is to emulate Java's static
members, so whenever you need something like static members accompanying a class, companion object
is definitely the choice to make.companion object
s always have a relation to their enclosing class, it depends on the situation whether that constitutes desirable semantics or is superfluous.companion object
is superfluous in comparison to a toplevel object
. Example:// this is unneccessary
class Foo {
companion object {
fun myStaticMember() { /*...*/ }
}
}
// refactor to:
object Foo {
fun myStaticMember() { /*...*/ }
}
open
and then making the companion object
extend it, basically to provide a "default instance" of the enclosing class:open class Foo(val value: Int) {
companion object Default : Foo(42)
}
val arbitrary = Foo(12)
val default = Foo
println(arbitrary.value) // 12
println(default.value) // 42
The tl;dr: If you want to realize something like a global service, go for object
. If you need static members accompanying a "regular" class, go for companion object
.