Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

builder syntax for collections in Kotlin

I'm booting up on Kotlin and looking to configure an Array of Labels in the pattern of a builder syntax. I came up with the Kotlin standard library function (apply) combined with a helper function on collections (forEach). Is it correct to refer to this as builder pattern? What that means to me is that the declaration, assignment and the configuration are done in one line/step. I appreciate any thoughts about how to write this in a still more compact and clear "Kotlin-ish" way, or is this the preferred Kotlin syntax more or less. BTW, there are a lot of ways to get this wrong (use let instead of apply does not return the receiver).

val labels = arrayOf(Label("A"),Label("B"),Label("C"),Label("D")).apply {
    this.forEach { it.prefWidth = 50.0 }
}
like image 388
J.E.Tkaczyk Avatar asked Feb 25 '18 17:02

J.E.Tkaczyk


2 Answers

What I'd suggest is avoiding repeating the word Label in your idiom:

val labels = arrayOf("A", "B", "C", "D")
        .map { Label(it).apply { prefWidth = 50.0 } }
        .toTypedArray()

This creates a bit more transient objects, but it reduces the noise and makes it easier to see the thing that varies between your labels.

like image 131
Marko Topolnik Avatar answered Sep 30 '22 00:09

Marko Topolnik


Instead of the explicit forEach, you can use a map with an inner apply, which was already correctly chosen in the original post:

arrayOf(Label("A"), Label("B"), Label("C"), Label("D"))
    .map { it.apply { prefWidth = 50.0 } }

Using apply in these cases is perfectly valid and even defined as an idiom here (Builder-style usage of methods that return Unit).

The shown code looks more readable to me although it creates a copy of the array inside map. In memory/performance critical situations, you might not want to pay this price and go with your already suggested solution instead.


As an other alternative, which might be useful if such actions are often needed in your code base, a simple extension function will help:

inline fun <T> Array<T>.applyEach(action: T.() -> Unit) = apply { forEach { it.action() } }

//usage
val labels = arrayOf(Label("A"), Label("B"), Label("C"), Label("D")).applyEach { prefWidth = 50.0 }
like image 40
s1m0nw1 Avatar answered Sep 30 '22 00:09

s1m0nw1