0
0
Kotlinprogramming~15 mins

With function behavior and use cases in Kotlin - Deep Dive

Choose your learning style9 modes available
Overview - With function behavior and use cases
What is it?
The 'with' function in Kotlin is a way to run multiple operations on the same object without repeating its name. It takes an object and a block of code, then executes the block with the object as its context. This helps write cleaner and shorter code when working with an object multiple times.
Why it matters
Without 'with', you would have to repeatedly write the object's name for every operation, making code longer and harder to read. 'With' solves this by grouping operations on one object, improving clarity and reducing mistakes. It makes your code look neat and easier to maintain, especially when configuring or modifying objects.
Where it fits
Before learning 'with', you should understand basic Kotlin syntax, functions, and how to work with objects. After mastering 'with', you can explore other Kotlin scope functions like 'apply', 'run', 'let', and 'also' to write more expressive and concise code.
Mental Model
Core Idea
The 'with' function lets you focus on one object by running a block of code where that object is the main focus, so you don't have to repeat its name.
Think of it like...
Imagine you have a toolbox and want to fix something. Instead of taking out each tool one by one and putting it back, you open the toolbox once and use all the tools inside before closing it. 'With' is like opening the toolbox once to use many tools without repeating the action.
with(object) {
  ┌─────────────────────────────┐
  │  block of code using object │
  │  without repeating its name  │
  └─────────────────────────────┘
}

Execution flow:
object → with → block runs with object as context → result
Build-Up - 6 Steps
1
FoundationBasic function and object usage
🤔
Concept: Understanding how to call functions and access properties on objects.
In Kotlin, you use the dot (.) to call functions or access properties on an object. For example: val person = Person("Anna", 25) println(person.name) person.greet() Here, 'person' is the object, 'name' is a property, and 'greet()' is a function.
Result
You can get or change data and call actions on objects by naming them explicitly.
Knowing how to access and use object members is the base for understanding why 'with' helps reduce repetition.
2
FoundationRepetitive object references problem
🤔
Concept: Recognizing that repeating the object name many times can make code long and less readable.
When you want to do many things with the same object, you write its name each time: val builder = StringBuilder() builder.append("Hello") builder.append(" World") builder.append("!") This repetition can clutter code and distract from the main logic.
Result
Code becomes longer and harder to read when the same object is repeated multiple times.
Seeing this problem motivates the need for a cleaner way to work with one object multiple times.
3
IntermediateUsing 'with' to simplify code
🤔Before reading on: do you think 'with' returns the object itself or the last expression inside the block? Commit to your answer.
Concept: 'With' runs a block of code with an object as its context and returns the block's last expression result.
You can rewrite repetitive code using 'with': val result = with(builder) { append("Hello") append(" World") append("!") toString() // last expression } println(result) // prints: Hello World! Inside the block, you can call methods directly without 'builder.'.
Result
Code is shorter and clearer, focusing on actions rather than repeating the object name.
Understanding that 'with' changes the context to the object lets you write cleaner code and that it returns the block's last value helps use it in expressions.
4
IntermediateDifference between 'with' and other scope functions
🤔Before reading on: do you think 'with' is an extension function or a regular function? Commit to your answer.
Concept: 'With' is a regular function that takes the object as an argument, unlike some other scope functions which are extensions.
'With' is called like this: with(object) { /* block */ } Other functions like 'apply' or 'run' are called on the object: object.apply { /* block */ } This difference affects how you use them and their return values.
Result
Knowing this helps choose the right scope function for your needs and understand subtle behavior differences.
Recognizing 'with' as a regular function clarifies why it needs the object as a parameter and how it differs from extension-based scope functions.
5
AdvancedUsing 'with' for object configuration
🤔Before reading on: do you think 'with' modifies the original object or creates a copy? Commit to your answer.
Concept: 'With' allows you to configure or modify an object inside the block, affecting the original object.
Example: val person = Person("John", 30) with(person) { name = "Jane" age = 28 } println(person.name) // Jane println(person.age) // 28 The changes inside 'with' affect the original 'person' object.
Result
You can use 'with' to group changes on an object clearly and concisely.
Knowing that 'with' works on the original object helps avoid confusion about object state and side effects.
6
ExpertPerformance and subtle behavior of 'with'
🤔Before reading on: do you think 'with' creates a new object or context each time it runs? Commit to your answer.
Concept: 'With' does not create a new object but creates a new function scope with the object as receiver, which can affect performance and debugging.
'With' is implemented as an inline function that takes the object and a lambda. It sets the lambda's receiver to the object, so inside the block, 'this' refers to the object. This means: - No new object is created. - The block runs in a new scope. - Debugging can show the lambda context. Because it's inline, there's minimal overhead, but understanding this helps when debugging or reading stack traces.
Result
You get efficient code with clear context but must be aware of scope changes for debugging.
Understanding the inline and receiver mechanics of 'with' explains its efficiency and how it changes the meaning of 'this' inside the block.
Under the Hood
'With' is a regular inline function that takes an object and a lambda with receiver. The lambda runs with the object as its receiver, so inside the lambda, you can call the object's members directly using 'this'. The function returns the lambda's last expression result. Because it's inline, the compiler replaces the call with the lambda code, avoiding function call overhead.
Why designed this way?
Kotlin designed 'with' to improve code readability by reducing repetition when working with one object. Making it inline avoids performance costs. It differs from extension scope functions to give developers options for different coding styles and return behaviors.
┌───────────────┐
│   Object O    │
└──────┬────────┘
       │ passed as argument
       ▼
┌─────────────────────┐
│  with(O, lambda)    │
│  ┌───────────────┐  │
│  │ lambda block   │  │
│  │ 'this' = O     │  │
│  │ calls on O     │  │
│  └───────────────┘  │
└─────────┬───────────┘
          │ returns last expression
          ▼
      Result value
Myth Busters - 4 Common Misconceptions
Quick: Does 'with' return the original object or the last expression inside the block? Commit to your answer.
Common Belief:Many think 'with' returns the original object it works on.
Tap to reveal reality
Reality:'With' returns the last expression inside the block, not the original object.
Why it matters:Assuming it returns the object can cause bugs when chaining calls or expecting the object after 'with'.
Quick: Is 'with' an extension function called on the object? Commit to your answer.
Common Belief:Some believe 'with' is an extension function called like object.with { }.
Tap to reveal reality
Reality:'With' is a regular function that takes the object as a parameter, not an extension function.
Why it matters:Misunderstanding this leads to confusion about syntax and when to use 'with' versus other scope functions.
Quick: Does 'with' create a copy of the object inside the block? Commit to your answer.
Common Belief:People sometimes think 'with' works on a copy, so changes inside don't affect the original object.
Tap to reveal reality
Reality:'With' works on the original object; changes inside the block modify it directly.
Why it matters:Wrong assumptions about copying can cause unexpected bugs when the original object changes unexpectedly.
Quick: Can 'with' be used with null objects safely? Commit to your answer.
Common Belief:Some think 'with' can be called on nullable objects without issues.
Tap to reveal reality
Reality:'With' requires a non-null object; calling it on null causes a runtime error.
Why it matters:Not checking for null before 'with' can crash programs, so null safety must be handled explicitly.
Expert Zone
1
Inside 'with', 'this' refers to the object, but if you need the outer 'this', you must qualify it explicitly.
2
'With' returns the last expression, so if you want to return the object itself for chaining, use 'apply' instead.
3
Because 'with' is inline, lambdas passed to it do not create additional objects, improving performance compared to non-inline functions.
When NOT to use
'With' is not suitable when you want to chain calls on the object because it returns the block result, not the object. In such cases, 'apply' or 'also' are better. Also, avoid 'with' on nullable objects without null checks; use safe calls or 'let' instead.
Production Patterns
In real-world Kotlin code, 'with' is often used to configure objects like builders, UI components, or data classes in a clear block. It's common in DSLs (domain-specific languages) to group related calls. However, developers prefer 'apply' when they want to return the object for chaining.
Connections
Extension functions in Kotlin
'With' contrasts with extension functions by being a regular function taking the object as a parameter, while extension functions add behavior directly to the object.
Understanding this difference clarifies Kotlin's flexible design for scope functions and how context is passed.
Fluent interfaces in software design
'With' helps write code that looks like fluent interfaces by grouping calls, but unlike fluent interfaces, it doesn't always return the object for chaining.
Knowing this helps decide when to use 'with' versus chaining methods for readable code.
Context managers in Python
Both 'with' in Kotlin and context managers in Python provide a way to run code blocks with a specific context or resource.
Seeing this cross-language pattern shows how programming languages solve the problem of managing context and reducing repetition.
Common Pitfalls
#1Expecting 'with' to return the original object for chaining.
Wrong approach:val result = with(builder) { append("Hello") append(" World") } result.append("!") // Error: 'result' is String, not StringBuilder
Correct approach:val result = with(builder) { append("Hello") append(" World") this // explicitly return the object } result.append("!") // Works
Root cause:Misunderstanding that 'with' returns the last expression, not the object itself.
#2Calling 'with' on a nullable object without null check.
Wrong approach:val person: Person? = null with(person) { name = "Anna" } // Throws NullPointerException
Correct approach:val person: Person? = null person?.let { with(it) { name = "Anna" } } // Safe call with null check
Root cause:Not handling nullability before using 'with', which requires a non-null object.
#3Using 'with' when you want to chain calls easily.
Wrong approach:val builder = StringBuilder() with(builder) { append("Hi") } .append("!") // Error: cannot chain after 'with'
Correct approach:val builder = StringBuilder() builder.apply { append("Hi") }.append("!") // Works with chaining
Root cause:Confusing 'with' return value with 'apply' which returns the object for chaining.
Key Takeaways
'With' lets you run multiple operations on one object without repeating its name, making code cleaner.
'With' is a regular inline function that takes the object as a parameter and returns the last expression inside its block.
Changes inside 'with' affect the original object, so it is useful for configuring or modifying objects.
'With' is not an extension function and does not support chaining by default; use 'apply' for chaining needs.
Always ensure the object passed to 'with' is not null to avoid runtime errors.