Kotlin Builder Pattern: Step-by-Step Guide with Code

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Why Builder in Kotlin is Not Just a "Copy-Paste" from Java?



If you've ever written in Java, you've certainly encountered the classic Builder pattern. Dozens of lines of code, inner static classes, set methods, and the final build(). It works, but it looks cumbersome.



Now imagine you've switched to Kotlin. A language designed to be concise and expressive. And then you face a task: you need to create an object with many parameters, some of which are optional. Your hand reaches out to write the classic Builder from Java...



Stop! In Kotlin, this pattern can be implemented 10 times simpler, more elegantly, and more safely. In this article, we'll break down 4 ways to implement the Builder pattern in Kotlin — from the simplest to the advanced DSL approach.



This material is based on a real Stack Overflow question that garnered thousands of views. We've adapted it for educational content so you can immediately apply the knowledge in practice.



The Problem: When is a Builder Needed?



Imagine you have a Pizza class:



class Pizza(    val size: String,    val cheese: Boolean = false,    val pepperoni: Boolean = false,    val mushrooms: Boolean = false,    val olives: Boolean = false,    val extraCheese: Boolean = false)


Seems fine. But when you create a pizza with 5 optional parameters, the code becomes unreadable:



val pizza = Pizza("Large", true, false, true, false, true)


Searching with your eyes to figure out which parameter means what is painful. This is where the Builder pattern comes to the rescue.



Method 1. Classic Java-style Builder (for compatibility)



If you need to support Java code or want a maximally explicit API, you can use the classic approach:



class Pizza private constructor(    val size: String,    val cheese: Boolean,    val pepperoni: Boolean,    val mushrooms: Boolean,    val olives: Boolean,    val extraCheese: Boolean) {    class Builder(private val size: String) {        private var cheese: Boolean = false        private var pepperoni: Boolean = false        private var mushrooms: Boolean = false        private var olives: Boolean = false        private var extraCheese: Boolean = false

fun cheese(value: Boolean) = apply { cheese = value } fun pepperoni(value: Boolean) = apply { pepperoni = value } fun mushrooms(value: Boolean) = apply { mushrooms = value } fun olives(value: Boolean) = apply { olives = value } fun extraCheese(value: Boolean) = apply { extraCheese = value }

fun build() = Pizza(size, cheese, pepperoni, mushrooms, olives, extraCheese) }}

// Usage:val pizza = Pizza.Builder("Large") .cheese(true) .pepperoni(true) .mushrooms(true) .build()


Pros: obvious code, understandable to Java developers.
Cons: lots of boilerplate code, doesn't use Kotlin's capabilities.



Method 2. Kotlin-style with named arguments and default values



The simplest and often best way is to not use a Builder at all, but rely on the language's own features:



data class Pizza(    val size: String,    val cheese: Boolean = false,    val pepperoni: Boolean = false,    val mushrooms: Boolean = false,    val olives: Boolean = false,    val extraCheese: Boolean = false)

// Usage:val pizza = Pizza( size = "Large", cheese = true, pepperoni = true, mushrooms = true)


This is clean, readable, and type-safe code. Kotlin's named arguments solve the problem of "magic numbers" and parameter order.



Blogs

Book Recommendations