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

Leave a Comment

Your email address will not be published. Required fields are marked *