:wave::skin-tone-4: I’m trying to understand if th...
# announcements
k
👋🏽 I’m trying to understand if there is a difference between the following blocks of code. Personally, I find that B is easier to read and understand in a sense that I don’t need to know what exactly
apply {}
does in order to understand the code. I am more curious to understand if there is some sort of tradeoff I’m missing here if one chooses A over B or vice versa. Thanks!
Copy code
// A
val setOfInts = HashSet<Int>().apply{
    for (i in 0..20) {
        this += i
    }
}

// B
val setOfInts = mutableSetOf<Int>()
for (i in 0..20) {
    setOfInts.add(i)
}
z
I would probably only use 1 if
setOfInts
was being defined as a property on a class, where the more imperative B wouldn’t be possible without adding an
init{}
block
although I would stick to the
+=
operator in B as well
k
👌 B is for sure more imperative to me.
setOfInts
is not a property on a class so would I really benefit from using
apply?
I can't think of a reason why I should use
apply
over += in B
e
Both should be pretty much equivalent, as scope functions are inlined. Another option is to use `also`:
Copy code
val setOfInts = HashSet<Int>().also { set ->
    for (i in 0..20) {
        set += i
    }
}
Going a bit further, the standard library offers some really nice functions for dealing with collections. Here's a guide on the subject. I highly recommend studying it as it boosts productivity quite a lot. For example, your snippet could be refactored as:
Copy code
val setOfInts = (0..20).mapTo(HashSet()) { it }
k
Ooo interesting, will take a look thanks!
s
(0..20).toHashSet()
😮 1
k
So based on the responses, there really isn’t any benefit of using a scope function like
apply {}
to populate a collection rather than doing it in an imperative way like B
e
Performance-wise, no.
apply
is useful to avoid repeating the receiver. For example:
Copy code
fun buildDataSource(dbConfig: DbConfig): DataSource {
	val dataSource = HikariDataSource()
	dataSource.jdbcUrl = dbConfig.url
	dataSource.username = dbConfig.user
	dataSource.password = dbConfig.password
	dataSource.idleTimeout = Duration.ofMinutes(2).toMillis()
	dataSource.maxLifetime = Duration.ofMinutes(10).toMillis()
	return dataSource
}
versus with `apply`:
Copy code
fun buildDataSource(dbConfig: DbConfig): DataSource {
	return HikariDataSource().apply {
		jdbcUrl = dbConfig.url
		username = dbConfig.user
		password = dbConfig.password
		idleTimeout = Duration.ofMinutes(2).toMillis()
		maxLifetime = Duration.ofMinutes(10).toMillis()
	}
}
💯 1
t
Another thing to consider is that you can use the approach with
A
to initialize a set mutably but not expose the mutable set to the outer scope.
Copy code
val setOfInts = mutableSetOf<Int>().apply {
  for(i in 0..20) {
    this += i
  }
}.toSet()
The type of
setOfInts
in this case is
Set
rather than
MutableSet
. Using
apply
allowed this to all be done in a single line of code.
I’m not sure if you’re particularly big on read-only interfaces but it is something my team makes widespread use of - code is much easier to reason about and maintenance is much simpler as it’s not as big of a worry that a developer unfamiliar with the codebase will accidentally change something we don’t intend to be changed.