Kotlin is a modern programming language that provides numerous features to write clean and concise code. Two such features are the run and with functions, which are used to perform operations on an object or set up a block of code. Although they appear similar, each has specific use cases that developers should understand to write effective Kotlin code. This article explores both functions, their differences, and when to use each.
The run Function
The run function is a standard library function in Kotlin used to execute a block of code and return its result. It's an extension function available for all objects in Kotlin, making it versatile for various scenarios. Here’s a basic example:
val result = "Kotlin".run {
println(this) // Output: Kotlin
length
}
println(result) // Output: 6
In this example, run is called on the string "Kotlin", executes the block, and then returns the length of the string, which is printed as 6.
Use Cases for run
Object Initialization: Often used when initializing objects that require several setup steps.
val config = MyConfig().run { setup() // Set up initial configurations enableLogging() this // Return the object itself after initialization }Executable Lambda: Act as a scope function executing a block of code without needing a preceding object.
val isEligible = run { val age = 20 val citizenship = "US" age >= 18 && citizenship == "US" } println(isEligible) // Output: true
The with Function
The with function, unlike run, is not an extension function. It takes a context object as its first parameter and then applies a block of operations to that object. It returns the result of the block’s last expression.
val list = mutableListOf("A", "B", "C")
with(list) {
println(size) // Output: 3
add("D")
println(this) // Output: [A, B, C, D]
}
In the example above, with is used to operate on list, adding elements and printing the result. The context object here is list, and operations within the with block are performed on it.
Use Cases for with
Object Configuration: Ideal for cases where successive operations on an object are needed.
val formattedText = with(StringBuilder()) { append("Hello, ") append("World!") toString() } println(formattedText) // Output: Hello, World!Multiple Object Properties Use: Simplifies operations that involve multiple properties of an object.
class Person(var firstName: String, var lastName: String) val person = Person("John", "Doe") val fullName = with(person) { "$firstName $lastName" } println(fullName) // Output: John Doe
Choosing Between run and with
The choice between run and with often depends on the context:
run: Use it when you need to operate on an object and still want the block to produce a useful result with 'this' as it context. It's also useful for initializing and configuring an object without an explicit context object.with: Ideal when you have a series of operations related to an object’s properties, especially where the context remains consistently the same, and when readability is enhanced by using a specific object without need of return object.
Understanding these functions illuminates their usefulness in Kotlin and improves code clarity and conciseness.