Inlining
Useful library functions
Collections vs Sequences
Agenda
Slide 3
Slide 3 text
Under the Hood
No performance overhead
Lambdas can be inlined
Slide 4
Slide 4 text
How inlining works?
Why inline?..
Slide 5
Slide 5 text
Useful library
functions
Slide 6
Slide 6 text
Useful library functions
run, let, takeIf, takeUnless, repeat
Slide 7
Slide 7 text
run function
val foo = run {
println("Calculating foo...")
"foo"
}
runs the block of code (lambda)
and returns the last expression
as the result
Slide 8
Slide 8 text
let function
fun getEmail(): String?
val email = getEmail()
if (email != null) sendEmailTo(email)
email?.let { e -> sendEmailTo(e) }
getEmail()?.let { sendEmailTo(it) }
allows to check the argument for being non-null,
not only the receiver
Slide 9
Slide 9 text
takeIf function
val number = 42
number.takeIf { it > 10 } // 42
val other = 2
other.takeIf { n -> n > 10 } // null
returns the receiver object if it satisfies the given predicate,
otherwise returns null
Slide 10
Slide 10 text
takeUnless function
val number = 42
number.takeUnless { it > 10 } // null
val other = 2
other.takeUnless { it > 10 } // 2
returns the receiver object if it does not satisfy the given
predicate, otherwise returns null
Slide 11
Slide 11 text
repeat function
repeat(10) {
println("Welcome!")
}
repeats an action for a given number of times
Slide 12
Slide 12 text
They all are declared as
inline functions
inline fun run(block: () -> R): R = block()
inline fun T.let(block: (T) -> R): R = block(this)
inline fun T.takeIf(predicate: (T) -> Boolean): T? =
if (predicate(this)) this else null
inline fun T.takeUnless(predicate: (T) -> Boolean): T? =
if (!predicate(this)) this else null
inline fun repeat(times: Int, action: (Int) -> Unit)
Slide 13
Slide 13 text
inline
Slide 14
Slide 14 text
inline function
compiler substitutes a body of
the function instead of calling it
Slide 15
Slide 15 text
inline fun T.takeUnless(predicate: (T) -> Boolean): T? =
if (!predicate(this)) this else null
fun foo(number: Int) {
val result = number.takeUnless { it > 10 }
...
}
Generated code (in the bytecode):
fun foo(number: Int) {
val result = if (!(number > 10)) number else null
...
}
inlined code
of lambda body
Slide 16
Slide 16 text
inline fun synchronized(lock: Lock, action: () -> T): T {
lock.lock()
try {
return action()
} finally {
lock.unlock()
}
}
fun foo(lock: Lock) {
synchronized(lock) {
println("Action")
}
}
fun foo(lock: Lock) {
lock.lock()
try {
println("Action")
} finally {
lock.unlock()
}
}
Generated code (in the bytecode):
inlined code
of lambda body
Slide 17
Slide 17 text
No performance overhead
when you use
run, let, takeIf, takeUnless, repeat
No anonymous class for lambda
Slide 18
Slide 18 text
Reified generics
Slide 19
Slide 19 text
Reified generics
startActivity("ANSWER" to 42)
val anys: List = listOf(1, "one", 2.0)
val strings: List =
anys.filterIsInstance() ["one"]
Slide 20
Slide 20 text
Inlining lambda body
val anys: List = listOf(1, "one", 2.0)
val strings: List =
anys.filterIsInstance()
inline fun List<*>.filterIsInstance(): List {
val destination = mutableListOf()
for (element in this) {
if (element is T) {
destination.add(element)
}
}
return destination
}
inline reified
["one"]
Slide 21
Slide 21 text
Inlining class reference
val intent = Intent(this, NewActivity::class.java)
intent.putExtra(params)
startActivity(intent)
startActivity("ANSWER" to 42)
inline fun Context.startActivity(
vararg params: Pair) {
val intent = Intent(this, T::class.java)
intent.putExtra(params)
startActivity(intent)
}
Generated code:
Slide 22
Slide 22 text
Collections
vs
Sequences
Slide 23
Slide 23 text
Extensions on collections are
inlined
inline fun Iterable.filter(predicate: (T) -> Boolean): List
inline fun Iterable.map(transform: (T) -> R): List
inline fun Iterable.any(predicate: (T) -> Boolean): Boolean
inline fun Iterable.find(predicate: (T) -> Boolean): T?
inline fun Iterable.groupBy(keySelector: (T) -> K): Map>
...
Slide 24
Slide 24 text
Collections vs Sequences
eager vs lazy
evaluation
Slide 25
Slide 25 text
Sequence
interface Sequence {
operator fun iterator(): Iterator
}
val sequence = listOf(1, 2, 3).asSequence()
for (i in sequence) { … }
val numbers = generateSequence(0) { it + 1 }
numbers.take(5).toList() [0, 1, 2, 3, 4]
Slide 26
Slide 26 text
Collections vs Sequences
Slide 27
Slide 27 text
Nothing happens until terminal operation is called
Slide 28
Slide 28 text
Order of operations is important
Slide 29
Slide 29 text
Collections vs Sequences
intermediate collections are created on chained calls
vs
lambdas are not inlined