Kotlin Object Expressions
Introduction
Object expressions in Kotlin provide a way to create anonymous objects - objects that are defined and instantiated in a single expression, without explicitly defining a class. If you're familiar with Java, you can think of them as similar to anonymous inner classes, but with a more concise syntax and additional capabilities.
Object expressions are particularly useful when you need to create a one-off implementation of an interface or extend a class without explicitly declaring a new named class.
Basic Syntax
The basic syntax of an object expression in Kotlin uses the object
keyword followed by an optional supertype (interface or class) and a body:
val myObject = object {
val property = "Hello"
fun method() = "World"
}
This creates an anonymous object with a property and a method. Here's how you can use it:
fun main() {
println(myObject.property) // Output: Hello
println(myObject.method()) // Output: World
}
Implementing Interfaces with Object Expressions
One of the most common uses of object expressions is to implement interfaces on the fly:
interface Greeter {
fun greet(): String
}
fun createGreeter(name: String): Greeter {
return object : Greeter {
override fun greet() = "Hello, $name!"
}
}
fun main() {
val greeter = createGreeter("Kotlin")
println(greeter.greet()) // Output: Hello, Kotlin!
}
In this example, we're creating an anonymous object that implements the Greeter
interface and providing an implementation for the greet()
function.
Extending Classes with Object Expressions
You can also use object expressions to create anonymous subclasses of existing classes:
open class Animal(val name: String) {
open fun makeSound(): String = "Some generic sound"
}
fun createDog(name: String): Animal {
return object : Animal(name) {
override fun makeSound() = "Woof!"
}
}
fun main() {
val dog = createDog("Buddy")
println("${dog.name} says: ${dog.makeSound()}")
// Output: Buddy says: Woof!
}
Accessing Variables from the Enclosing Scope
Object expressions can access variables from the enclosing scope, unlike Java's anonymous inner classes which can only access final variables:
fun createCounter(startValue: Int): () -> Int {
var count = startValue
return object {
fun next() = ++count
}::next
}
fun main() {
val counter = createCounter(5)
println(counter()) // Output: 6
println(counter()) // Output: 7
println(counter()) // Output: 8
}
In this example, the object expression captures the mutable count
variable from its surrounding scope.
Multiple Interface Implementation
Object expressions can implement multiple interfaces at once:
interface Clickable {
fun click()
}
interface Focusable {
fun focus()
}
val button = object : Clickable, Focusable {
override fun click() {
println("I was clicked")
}
override fun focus() {
println("I got focus")
}
fun sayHi() = println("Hi there!")
}
fun main() {
button.click() // Output: I was clicked
button.focus() // Output: I got focus
button.sayHi() // Output: Hi there!
}
Object Expressions vs. Object Declarations
It's important to distinguish between object expressions (what we've been discussing) and object declarations. Object expressions create instances that are used right away, while object declarations define singletons that can be referenced by name.
// Object expression - creates an instance
val listener = object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// handle click
}
}
// Object declaration - creates a singleton
object GlobalConfig {
var debugging = false
}
Practical Example: Event Listeners
One common use case for object expressions is creating event listeners, especially in UI programming:
// Assuming we have a Button class
class Button {
private var clickListener: (() -> Unit)? = null
fun setOnClickListener(listener: () -> Unit) {
clickListener = listener
}
fun click() {
clickListener?.invoke()
}
}
fun main() {
val button = Button()
// Using an object expression to handle the click event
button.setOnClickListener {
println("Button was clicked!")
}
button.click() // Output: Button was clicked!
}
Practical Example: Custom Comparators
Another common use is creating custom comparators for sorting:
data class Person(val name: String, val age: Int)
fun main() {
val people = listOf(
Person("Alice", 29),
Person("Bob", 31),
Person("Charlie", 25)
)
// Sort using an anonymous Comparator
val sortedByAge = people.sortedWith(
object : Comparator<Person> {
override fun compare(p1: Person, p2: Person): Int {
return p1.age - p2.age
}
}
)
println(sortedByAge)
// Output: [Person(name=Charlie, age=25), Person(name=Alice, age=29), Person(name=Bob, age=31)]
}
This could be shortened using Kotlin's SAM (Single Abstract Method) conversion, but the object expression version clearly shows the concept.
Summary
Kotlin object expressions provide a powerful way to create anonymous objects on the fly. They are useful for:
- Creating one-time implementations of interfaces
- Creating anonymous subclasses of existing classes
- Implementing event listeners
- Creating custom comparators
- Any situation where you need an object with custom behavior but don't want to define a named class
Unlike Java's anonymous inner classes, Kotlin object expressions can access and modify variables from their enclosing scope, making them more flexible.
Exercises
-
Create an object expression that implements a
Runnable
interface and prints "Running!" when itsrun()
method is called. -
Define a function
createLogger(tag: String)
that returns an object with methodsinfo(message: String)
anderror(message: String)
which print formatted log messages with the given tag. -
Use an object expression to create a custom filter for a list of numbers that only lets even numbers pass through.
Additional Resources
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)