Effective Kotlin: Item 2 — Consider a builder when faced with many constructor parameters

The Builder pattern is promoted in Effective Java to help work around the telescoping constructor anti-pattern when you end up with numerous constructors due to optional parameters. See Joshua’s book for the pros and cons of using the Builder pattern.

We see the pattern loosely in use in Java’s StringBuilder. In Android the AlertDialog.Builder() is a more traditional implementation. IntelliJ also provides the built-in Replace Constructor with Builder refactoring to help implement these in Java.

In Kotlin we have default and named arguments which can alleviate the need to use a Builder. For example, a burger is made up of some patties and optionally cheese and tomato.

class Burger(
val patties: Int,
val hasCheese: Boolean = false,
val hasTomato: Boolean = false
)

val doubleCheeseBurger = Burger(patties = 2, hasCheese = true)

Of course, this works great in a 100% Kotlin world. For Java interoperability, we may use the @JvmOverloads annotation which makes the compiler generate the same telescoping constructors Joshua talks about trying to avoid, so you certainly have to consider your use case before ditching a builder.

If interoperability isn’t a concern, then another alternative to default and named arguments is to implement a type-safe builder, also known as Kotlin DSL (domain-specific language). If the official documentation doesn’t make sense, Writing DSLs in Kotlin part 1 and part 2 explains how to create your own. It is also worth checking out Village DSL for a repository of Kotlin DSL designs.

Defining a DSL can allow us to create our burger as follows:

val doubleCheeseBurger = burger(patties = 2) {
toppings {
+Cheese
}
}

With the following code used to implement the DSL:

data class Burger(val patties: Int, val toppings: List<Toppings>)
enum class Toppings { Cheese, Tomato }

fun burger(patties: Int, init: BurgerBuilder.() -> Unit) =
BurgerBuilder(patties).apply(init).build()

class BurgerBuilder(private val patties: Int) {
private var toppings: List<Toppings> = mutableListOf()

fun toppings(init: List<Toppings>.() -> Unit) {
toppings.apply(init)
}

operator fun Toppings.unaryPlus() {
toppings += this
}

fun build() = Burger(patties, toppings)
}

Each week I am looking at “items” from Joshua Bloch’s well-respected book, Effective Java to see how it applies to Kotlin. You can find the rest of the items I’ve covered at Effective Kotlin. Please let me know your thoughts.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store