SyntaxStudy
Sign Up
Kotlin Object Declarations and Companion Objects
Kotlin Beginner 1 min read

Object Declarations and Companion Objects

Kotlin replaces Java singletons with the object declaration: a class and its single instance are defined simultaneously using the object keyword. Object declarations are lazily initialised on first access and are thread-safe by the JVM's class-loading guarantee. Companion objects are object declarations scoped to a class. Members of a companion object are accessed with the class name as a qualifier, giving the appearance of static members without actually using Java's static keyword. Companion objects can implement interfaces and be extended with extension functions. Object expressions (anonymous objects) provide ad-hoc implementations of interfaces or subclasses, similar to Java's anonymous classes. Unlike object declarations they are not singletons: each evaluation of the expression creates a new instance.
Example
// Singleton via object declaration
object AppConfig {
    var debug = false
    val version = "2.0"
    fun log(msg: String) { if (debug) println("[DEBUG] $msg") }
}

// Class with companion object (static-like factory)
class Database private constructor(val url: String) {

    companion object {
        private var instance: Database? = null

        fun getInstance(url: String): Database {
            return instance ?: Database(url).also { instance = it }
        }

        // Companion can implement an interface
        fun create(url: String) = Database(url)
    }

    fun query(sql: String) = println("Querying [$url]: $sql")
}

// Object expression — anonymous singleton implementing an interface
interface ClickListener {
    fun onClick(id: Int)
}

fun setListener(listener: ClickListener) = listener.onClick(42)

fun main() {
    AppConfig.debug = true
    AppConfig.log("App started v${AppConfig.version}")

    val db = Database.getInstance("jdbc:sqlite:app.db")
    db.query("SELECT * FROM users")

    // Second call returns same instance
    val db2 = Database.getInstance("jdbc:sqlite:app.db")
    println("Same instance: ${db === db2}")

    setListener(object : ClickListener {
        override fun onClick(id: Int) = println("Clicked: $id")
    })
}