Objects and Companion Objects
Prerequisites: Interfaces
The object keyword creates a class and its single instance in one
step.
Object declarations — singletons
A singleton is a class with exactly one instance, shared by the whole program:
object AppConfig {
const val VERSION = "1.4.0"
var debug = false
fun summary() = "v$VERSION (debug=$debug)"
}
fun main() {
AppConfig.debug = true
println(AppConfig.summary())
}
v1.4.0 (debug=true)
You use it by name — there's no constructor to call. It's created the first time it's used.
Companion objects
A companion object holds members that belong to the class itself
rather than to instances — like static members in Java. A common use
is factory functions: alternative, named ways to create objects.
class User private constructor(val name: String, val email: String) {
companion object {
fun fromEmail(email: String): User {
val name = email.substringBefore("@").replaceFirstChar { it.uppercase() }
return User(name, email)
}
}
}
fun main() {
val user = User.fromEmail("ada@example.com")
println("${user.name} <${user.email}>")
}
Ada <ada@example.com>
The private constructor forces everyone to create users through
fromEmail.
Object expressions — one-off objects
object : SomeInterface { ... } creates an anonymous object on the
spot:
interface Greeter {
fun greet(name: String): String
}
fun main() {
val formal = object : Greeter {
override fun greet(name: String) = "Good evening, $name."
}
println(formal.greet("Ada"))
}
Good evening, Ada.
Data objects
data object gives a singleton a clean toString — handy in sealed
hierarchies (a later lesson):
data object Loading
fun main() {
println(Loading)
}
Loading
Common mistakes
- Putting mutable state in singletons and using them everywhere. Global mutable state makes code hard to test and reason about; pass dependencies in instead.