Object Expressions & Declarations

Reviewed & published by Brayan K

An object declaration creates a single shared instance (a singleton), while an object expression creates an anonymous one-off object on the spot, much like an anonymous class.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

You'll learn the singleton object declaration, the anonymous object : Type expression, how a companion object differs, and implementing an interface inline.

What You'll Learn in This Lesson

1️⃣ Object Declarations: One Shared Instance

An object declaration — object Name { ... } — defines a singleton: exactly one instance, created the first time you use it. There's no constructor and no new; you access members straight through the name. It's perfect for global state, services, and configuration.

// An object DECLARATION creates a SINGLETON: exactly one instance,
// created lazily the first time you touch it. No constructor, no 'new'.
object Counter {
    var count = 0                 // shared state for the whole program
    fun increment() { count++ }
}

fun main() {
    Counter.increment()
    Counter.increment()
    Counter.increment()
    println(Counter.count)        // -> 3

    // It is the SAME object everywhere — references are identical:
    val a = Counter
    val b = Counter
    println(a === b)              // -> true
}

Because there's only ever one Counter, two references to it are identical (=== is true) and they share the same count.

2️⃣ Object Expressions: Anonymous, One-Off Objects

An object expression — object : Type { ... } used as a value — creates a fresh anonymous object right where you need it, like Java's anonymous classes. It's the idiomatic way to pass a quick one-off implementation of an interface into a function.

// An object EXPRESSION is an anonymous object created on the spot —
// like an anonymous class. Here it implements an interface inline.
interface ClickListener {
    fun onClick(): String
}

fun register(listener: ClickListener): String {
    return listener.onClick()
}

fun main() {
    // Create a one-off implementation without naming a class:
    val result = register(object : ClickListener {
        override fun onClick(): String = "clicked once"
    })
    println(result)                    // -> clicked once

    // An object expression can also just bundle a few ad-hoc values:
    val point = object {
        val x = 3
        val y = 4
    }
    println("${point.x}, ${point.y}")   // -> 3, 4
}

Unlike a declaration, an expression runs every time the line executes and produces a new object each time — there's no single shared instance.

3️⃣ Companion Objects: A Singleton Inside a Class

A companion object is an object declared inside a class with the companion keyword. Its members are accessed through the class name — the closest Kotlin gets to Java's static. Use it for factory functions and constants that belong to the class itself.

// A COMPANION OBJECT lives INSIDE a class and is tied to that class.
// Use it for factory methods and constants — like 'static' in Java.
class User private constructor(val name: String) {
    companion object {
        const val ROLE = "guest"
        // A factory function that creates instances:
        fun create(name: String): User = User(name)
    }
}

fun main() {
    // Call companion members through the class name, no instance needed:
    val u = User.create("Ada")
    println(u.name)        // -> Ada
    println(User.ROLE)     // -> guest
}

Choose a companion object when the singleton belongs to one class (factories, constants); choose a standalone object declaration when it's an independent global like a logger.

Your turn. Fill in the ___, then run and compare.

// 🎯 YOUR TURN — replace the ___ then run main()
// Make a singleton that stores app settings.
___ AppConfig {                  // 👉 put "object" in the blank
    var theme = "light"
    fun isDark() = theme == "dark"
}

fun main() {
    println(AppConfig.theme)     // ✅ Expected: light
    AppConfig.theme = "dark"
    println(AppConfig.isDark())  // ✅ Expected: true
}

Mini-Challenge: Logger + Inline Handler

Combine a singleton object declaration with an inline object expression implementing an interface.

// 🎯 MINI-CHALLENGE: a logger singleton + an inline listener.
// Save as Logger.kt and run main().
//
// 1. Make an object declaration Logger with a fun log(msg: String)
//    that prints "LOG: " + msg.
// 2. Define interface Handler { fun handle(): String }.
// 3. In main(), create an inline object expression implementing Handler
//    whose handle() returns "handled". Log the result with Logger.log(...).
//
// ✅ Expected output:
//    LOG: handled

fun main() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Objects

ConceptKotlin Syntax
Singletonobject Counter { ... }
Access a memberCounter.increment()
Anonymous objectobject : Listener { ... }
Ad-hoc bundleobject { val x = 1 }
Companion objectcompanion object { ... }
Companion callUser.create("Ada")

🎉 Lesson Complete!

Practice quiz

What does an object declaration like object Counter { } create?

  • A new instance every time it is referenced
  • A singleton: exactly one shared instance
  • An abstract class
  • A companion object

Answer: A singleton: exactly one shared instance. An object declaration is a singleton with one instance created lazily on first access.

When is a Kotlin object declaration initialized?

  • At program startup, always
  • Lazily, the first time it is accessed
  • Each time it is referenced
  • Only when you call new on it

Answer: Lazily, the first time it is accessed. Object declarations are initialized lazily and safely on first access.

Given val a = Counter and val b = Counter, what does a === b print?

  • false
  • It does not compile
  • true
  • It throws at runtime

Answer: true. There is only one Counter instance, so both references are identical.

What does an object expression such as object : Listener { } produce?

  • A singleton shared across the program
  • A companion object
  • A fresh anonymous object each time the line runs
  • A constant

Answer: A fresh anonymous object each time the line runs. An object expression creates a new anonymous object every time it is evaluated.

How do you access a member of a companion object?

  • Through an instance, like user.create()
  • Through the class name, like User.create()
  • With the new keyword
  • Only inside the constructor

Answer: Through the class name, like User.create(). Companion members are accessed through the class name, similar to Java statics.

What is a companion object best used for?

  • Creating many instances quickly
  • Factory functions and constants tied to a class
  • Replacing interfaces
  • Holding thread-local data only

Answer: Factory functions and constants tied to a class. Companion objects hold factory methods and constants that belong to the class.

Why does object X(val a: Int) fail to compile?

  • Objects cannot have properties
  • Object declarations take no constructor parameters
  • val is not allowed in objects
  • It needs the new keyword

Answer: Object declarations take no constructor parameters. Object declarations have no constructor; initialize state in the body instead.

Which is the idiomatic Kotlin replacement for a Java anonymous class?

  • A companion object
  • A data class
  • An object expression
  • A sealed class

Answer: An object expression. Object expressions are Kotlin's clean replacement for Java anonymous classes.

Can an object declaration implement an interface?

  • No, only classes can
  • Yes, it can implement interfaces and act as a singleton service
  • Only if it is a companion object
  • Only inside another object

Answer: Yes, it can implement interfaces and act as a singleton service. Both object declarations and expressions can implement interfaces.

How do you call increment() on object Counter { fun increment() }?

  • Counter().increment()
  • new Counter().increment()
  • Counter.increment()
  • Counter::increment()

Answer: Counter.increment(). Access object members directly through the name; there is no constructor call.

Continue this course

Frequently asked questions

What is the difference between an object declaration and an object expression?

An object declaration uses object Name { ... } at the top level or inside a class and creates a single, named instance — a singleton — that the whole program shares. An object expression uses object : SomeType { ... } as a value inside a function and creates a fresh anonymous object every time that line runs, much like an anonymous class in Java. Declarations are for 'there is exactly one of these'; expressions are for 'I need a quick one-off implementation right here'.

When should I use a companion object versus an object declaration?

Use a companion object when the members logically belong to a particular class — factory methods, constants, or anything you would have written as static. It is declared inside the class and accessed through the class name, like User.create(...). Use a standalone object declaration when the singleton is its own standalone thing not tied to one class, such as a global Counter, a Logger, or an app-wide configuration holder.

Is an object declaration thread-safe and when is it created?

A Kotlin object declaration is initialized lazily and safely the first time it is accessed, and there is guaranteed to be only one instance for the lifetime of the program. That makes it a convenient, correct singleton without the boilerplate of double-checked locking. Because the single instance holds shared mutable state if you give it any, treat that state with the same care you would any global — concurrent writes still need their own synchronization.

Can an object implement an interface or extend a class?

Yes. Both object declarations and object expressions can implement one or more interfaces and extend a class, providing the required members in the body. This is extremely common: an object expression is the idiomatic way to pass a small, one-off implementation of a listener or comparator interface to a function, and a named object declaration can implement an interface to act as a singleton service.