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
- Creating a singleton with object declaration
- Why an object is the same instance everywhere
- Anonymous object expressions on the spot
- Implementing an interface inline
- How a companion object differs
- When to use each form
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)
- ❌ Trying to construct an object with () — Counter() is wrong; there's no constructor. ✅ Use the name directly: Counter.increment().
- ❌ Giving an object declaration a constructor — object X(val a: Int) won't compile. ✅ Objects take no constructor parameters; initialize in the body.
- ❌ Expecting an object expression to be a singleton — Each evaluation makes a new object. ✅ Use an object declaration when you need exactly one instance.
- ❌ Calling a companion member on an instance — Access it via the class name. ✅ Write User.create(...), not someUser.create(...).
Pro Tips
- 💡 Objects can implement interfaces: a singleton service can satisfy a contract just like a class.
- 💡 Companion for factories: hide the constructor with private constructor and expose a create().
- 💡 Object expressions for listeners: they're Kotlin's clean replacement for Java anonymous classes.
📋 Quick Reference — Objects
| Concept | Kotlin Syntax |
|---|---|
| Singleton | object Counter { ... } |
| Access a member | Counter.increment() |
| Anonymous object | object : Listener { ... } |
| Ad-hoc bundle | object { val x = 1 } |
| Companion object | companion object { ... } |
| Companion call | User.create("Ada") |
🎉 Lesson Complete!
- ✅ An object declaration is a lazily-created singleton
- ✅ The same object instance is shared everywhere (=== is true)
- ✅ An object expression is an anonymous one-off object
- ✅ Objects can implement interfaces inline
- ✅ A companion object ties a singleton to a class for factories and constants
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
- Previous: Interfaces & Abstract Classes
- Next: Delegation & Delegated Properties (by) — Class delegation and delegated properties like lazy, observable, and by map
- Quick reference: Kotlin cheat sheet
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.