Kotlin has no static keyword; objects and companions are how you write singletons, factories and constants.
An object declaration defines a class and its single instance at once, a thread-safe, lazily initialized singleton:
object Registry { val items = mutableListOf<String>() }
Registry.items.add("a")Objects can implement interfaces and extend classes, which makes them handy for stateless strategies and comparators.
A companion object lives inside a class and holds members that belong to the class rather than to instances, Kotlin's replacement for static:
class User private constructor(val name: String) {
companion object { fun of(raw: String) = User(raw.trim()) }
}
User.of(" Ada ")Typical uses: factory functions (often with a private constructor), constants (const val MAX = 10), and loggers. A companion can have a name and implement interfaces. For Java callers, annotate with @JvmStatic or @JvmField to get true static members.
An object expression creates an instance of an anonymous class on the spot, like Java's anonymous inner classes:
val cmp = object : Comparator<String> { override fun compare(a: String, b: String) = a.length - b.length }Data objects (data object Loading) give a singleton a nice toString, which is useful in sealed hierarchies.
Remember that singletons are global state: keep them stateless or inject dependencies instead of reaching for a global object everywhere, which makes testing hard.
Example
object IdGenerator { // singleton
private var next = 1
fun newId() = next++
}
class User private constructor(val id: Int, val name: String) {
companion object {
const val MAX_NAME = 20
fun of(raw: String): User {
require(raw.length <= MAX_NAME)
return User(IdGenerator.newId(), raw.trim())
}
}
override fun toString() = "User($id, $name)"
}
data object Loading
fun main() {
println(User.of(" Ada ")) // User(1, Ada)
println(User.of("Bo")) // User(2, Bo)
println(User.MAX_NAME) // 20
val byLength = object : Comparator<String> {
override fun compare(a: String, b: String) = a.length - b.length
}
println(listOf("ccc", "a", "bb").sortedWith(byLength)) // [a, bb, ccc]
println(Loading) // Loading
}Remember
- object Name declares a lazily created, thread-safe singleton
- companion object holds class-level members such as factories and constants
- object : Interface { } creates anonymous instances
- Use @JvmStatic for static access from Java
Watch out for
- Putting mutable global state in objects, which leaks between tests.