Questions
Answer
Builder Pattern in Kotlin = “construct a complex object step-by-step, without a 12-parameter constructor turning into a crime scene.”
In interviews, they’re checking 3 things:
- you know why builders exist
- you can implement one in Kotlin idiomatically
- you know when not to use it (because Kotlin has better tools)
What is Builder Pattern?
A Builder separates how you build an object from the object itself.
Use it when:
- Object has many optional parameters
- There are validation rules
- You want readable, named configuration
- Construction is multi-step (e.g., build request → add headers → add body → validate → create)
Classic Java-style builders were created to avoid:
```java new User("Akshay", null, null, null, 0, false, null, ...) ```
Kotlin’s “default args” vs Builder (important interview point)
Kotlin often removes the need for builders because we can do:
```kotlin data class User( val name: String, val age: Int = 0, val city: String? = null, val isPro: Boolean = false ) ```
So: Builder is NOT always needed in Kotlin.
But builder still wins when:
- You must enforce rules (e.g. if `isPro == true` then `subscriptionId` must exist)
- You want a fluent DSL for configuration
- You’re creating immutable objects with many “optional but not really optional” fields
- You are interacting with Java libs / APIs that expect builder style
1) Classic Builder in Kotlin (Interview-Friendly)
Scenario: building a `NetworkRequest`
You want:
- required: url
- optional: method, headers, body, timeout
- validation: GET can’t have body, timeout must be positive
```kotlin class NetworkRequest private constructor( val url: String, val method: String, val headers: Map<String, String>, val body: String?, val timeoutMs: Long ) { class Builder(private val url: String) { private var method: String = "GET" private val headers: MutableMap<String, String> = mutableMapOf() private var body: String? = null private var timeoutMs: Long = 10_000
fun method(value: String) = apply {
method = value.uppercase()
}
fun addHeader(key: String, value: String) = apply {
headers[key] = value
}
fun body(value: String?) = apply {
body = value
}
fun timeoutMs(value: Long) = apply {
timeoutMs = value
}
fun build(): NetworkRequest {
require(url.startsWith("http")) { "Invalid url: $url" }
require(timeoutMs > 0) { "Timeout must be > 0" }
if (method == "GET") require(body == null) { "GET request can't have a body" }
return NetworkRequest(
url = url,
method = method,
headers = headers.toMap(), // immutable copy
body = body,
timeoutMs = timeoutMs
)
}
}
} ```
Usage:
```kotlin val request = NetworkRequest.Builder("https://api.masterly.app/sessions") .method("POST") .addHeader("Authorization", "Bearer token") .addHeader("Content-Type", "application/json") .body("""{"skillId":"compose","minutes":30}""") .timeoutMs(15_000) .build() ```
Interview points to say out loud:
- `private constructor` forces creation only via Builder (immutability control)
- `apply` returns the builder itself → fluent chain
- `build()` is where validation lives
- immutable copy of `headers` prevents accidental mutation after build
2) Kotlin DSL Builder (Idiomatic + Sexy)
Kotlin lets you write builders that feel like config blocks.
```kotlin class NetworkRequest private constructor( val url: String, val method: String, val headers: Map<String, String>, val body: String?, val timeoutMs: Long ) { class Builder { lateinit var url: String var method: String = "GET" var body: String? = null var timeoutMs: Long = 10_000 private val headers: MutableMap<String, String> = mutableMapOf()
fun header(key: String, value: String) {
headers[key] = value
}
fun build(): NetworkRequest {
check(::url.isInitialized) { "url is required" }
require(timeoutMs > 0) { "Timeout must be > 0" }
val m = method.uppercase()
if (m == "GET") require(body == null) { "GET request can't have a body" }
return NetworkRequest(
url = url,
method = m,
headers = headers.toMap(),
body = body,
timeoutMs = timeoutMs
)
}
}
}
fun networkRequest(block: NetworkRequest.Builder.() -> Unit): NetworkRequest { return NetworkRequest.Builder().apply(block).build() } ```
Usage:
```kotlin val request = networkRequest { url = "https://api.masterly.app/sessions" method = "POST" header("Authorization", "Bearer token") header("Content-Type", "application/json") body = """{"skillId":"compose","minutes":30}""" timeoutMs = 15_000 } ```
Interview one-liner: “This is Builder Pattern using Kotlin DSL: the builder is configured via a lambda with receiver.”
3) Builder vs `copy()` (data class style)
For immutable data classes, `copy()` is often the “micro-builder”:
```kotlin data class User( val name: String, val city: String? = null, val age: Int = 0 )
val u1 = User(name = "Akshay") val u2 = u1.copy(city = "Bangalore", age = 26) ```
So you say: “If it’s just optional params and immutability, Kotlin default args + data class `copy()` often beats builders.”
Real Android examples you can mention in interview
- OkHttp: `Request.Builder()`, `OkHttpClient.Builder()`
- NotificationCompat.Builder
- AlertDialog.Builder
- WorkManager: `OneTimeWorkRequestBuilder`, constraints builder-ish patterns
These are builders because configuration has many options + readable setup.
Common interview questions + crisp answers
Q: Why Builder Pattern? A: To construct complex objects step-by-step, avoid telescoping constructors, enforce validation, and keep objects immutable.
Q: Why not always use Builder in Kotlin? A: Kotlin has named params + default args + data class `copy()`, so builder is only needed when validation / multi-step construction / DSL readability is important.
Q: How do you make sure the built object is immutable? A: Private constructor + builder holds mutable state + `build()` returns object with `val` fields + defensive copies for collections (`toMap()`).
Q: Where do you put validation? A: In `build()`. Builders are like “form filling”; `build()` is “submit & validate”.
1:1 Mentorship
Get personalized guidance from a Google Developer Expert. Accelerate your career with dedicated support.
Help fellow developers prepare for interviews
Sharing helps the Android community grow 💚