Kotlin Object Keyword
The object keyword in Kotlin creates a singleton — a class with exactly one instance that is created automatically. You never call a constructor for an object. It is also used for anonymous objects and to create companion objects.
Singleton Object
object AppConfig {
val version = "2.4.1"
val debug = false
var language = "en"
fun info() = "App v$version | debug=$debug | lang=$language"
}
fun main() {
println(AppConfig.info()) // App v2.4.1 | debug=false | lang=en
AppConfig.language = "fr"
println(AppConfig.language) // fr
// AppConfig and AppConfig are the SAME instance
println(AppConfig === AppConfig) // true
}Why Singleton?
Without singleton:
val config1 = AppConfig() // instance 1
val config2 = AppConfig() // instance 2 — DIFFERENT object
config1.language = "fr"
config2.language // still "en" — they don't share state!
With singleton (object):
AppConfig.language = "fr"
AppConfig.language // "fr" — single shared instance
Object for Utility Functions
object MathUtils {
fun square(n: Double) = n * n
fun cube(n: Double) = n * n * n
fun isPrime(n: Int): Boolean {
if (n < 2) return false
for (i in 2..Math.sqrt(n.toDouble()).toInt()) {
if (n % i == 0) return false
}
return true
}
}
fun main() {
println(MathUtils.square(5.0)) // 25.0
println(MathUtils.cube(3.0)) // 27.0
println(MathUtils.isPrime(17)) // true
println(MathUtils.isPrime(18)) // false
}Object Inheriting from a Class or Interface
interface Logger {
fun log(message: String)
}
object ConsoleLogger : Logger {
override fun log(message: String) {
println("[LOG] $message")
}
}
object FileLogger : Logger {
override fun log(message: String) {
println("[FILE] Writing: $message")
}
}
fun main() {
ConsoleLogger.log("App started")
FileLogger.log("User login event")
}Anonymous Objects
Anonymous objects create a one-off object without naming the class. Useful for implementing an interface inline:
interface ClickListener {
fun onClick()
}
fun setListener(listener: ClickListener) {
listener.onClick()
}
fun main() {
setListener(object : ClickListener {
override fun onClick() {
println("Button was clicked!")
}
})
}Object Declaration Lifecycle
object Database {
init {
println("Database initialized") // runs once, on first access
}
val connection = "Connected to db"
}
fun main() {
println("Before access")
println(Database.connection) // triggers initialization
println(Database.connection) // no re-initialization
}
Output:
Before access
Database initialized
Connected to db
Connected to db
Practical Example: Event Bus
object EventBus {
private val listeners = mutableMapOf Unit>>()
fun subscribe(event: String, action: (String) -> Unit) {
listeners.getOrPut(event) { mutableListOf() }.add(action)
}
fun publish(event: String, data: String) {
listeners[event]?.forEach { it(data) }
}
}
fun main() {
EventBus.subscribe("order_placed") { data -> println("Email service: $data") }
EventBus.subscribe("order_placed") { data -> println("Inventory: $data") }
EventBus.subscribe("user_login") { data -> println("Security: $data") }
EventBus.publish("order_placed", "Order #5001 received")
EventBus.publish("user_login", "User alice logged in")
} Output:
Email service: Order #5001 received
Inventory: Order #5001 received
Security: User alice logged in