Slide 1

Slide 1 text

The power of inline

Slide 2

Slide 2 text

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

Slide 30

Slide 30 text

Copyright © 2017 https://github.com/JetBrains/kotlin-workshop