В Kotlin существует модификатор inline
, которым можно пометить функцию. Основное его предназначение - повысить производительность. Чтобы понять за счёт чего она повышается, нужно вспомнить лямбда-выражения.
Как правило, лямбда-выражения компилируются в анонимные классы. То есть каждый раз, когда используется лямбда-выражение, создаётся дополнительный класс. Отсюда вытекают дополнительные накладные расходы у функций, которые принимают лямбду в качестве аргумента. Если же функцию отметить модификатором inline
, то компилятор не будет создавать анонимные классы и их объекты для каждого лямбда-выражения, а просто вставит код её реализации в место вызова. Или другими словами встроит её.
1
2
3
inline fun <T> lock(lock: Lock, body: () -> T): T {
// ...
}
Модификатор inline
влияет и на функцию, и на лямбду, переданную ей: они обе будут встроены в место вызова.
Если же вы хотите, чтобы некоторые лямбды, переданные inline-функции, не были встроены, то отметьте их модификатором noinline
.
1
2
3
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
Разница между ними в том, что встраиваемая лямбда может быть вызвана только внутри inline-функции, либо может быть передана в качестве встраиваемого аргумента. В то время как noinline-лямбды можно хранить внутри полей, передавать куда-либо итд.
Return
Иначе работает и оператор return
. В лямбда-выражении оператор return
завершает работу всей функции, в которой было вызвано лямбда-выражение. Для выхода только из лямбды используется label
.
Но если лямбда-выражение передаётся невстраиваемой функции, то использование оператора return
недопустимо. Если же лямбда-выражение передаётся в inline-функцию, то оператор return
разрешён и он завершает работу этой функции.
Reified
Reified - это ключевое слово, которое может быть использовано только в inline-функциях. Его цель - получение доступа к информации о типе класса.
Параметры, отмеченные этим ключевым словом, ещё называют овеществляемыми.
Допустим у нас есть такая функция:
1
2
3
inline fun <T> genericsExample(value: T) {
println(value)
}
Она универсальная и может быть использована для любого типа переменной. Такой эффект достигнут благодаря использованию дженериков (обобщения). Если мы захотим узнать тип класса T
,
1
2
3
4
inline fun <T> genericsExample(value: T) {
println(value)
println("Type of T: ${T::class.java}")
}
то получим ошибку Cannot use 'T' as reified type parameter
, так как информация о типе в дженериках отсутствует.
Тут нам на помощь и приходит reified
. Ключевое слово указывается перед типом, информацию о котором мы хотим получить внутри функции.
1
2
3
4
inline fun <reified T> genericsExample(value: T) {
println(value)
println("Type of T: ${T::class.java}")
}
За кулисами компилятор заменит тип T
на фактический, поэтому мы сможем получить о нём информацию и при этом нет необходимости этот тип явно передавать функции.
Также reified
может быть использован в другом сценарии: для возврата из функции разных типов данных.
1
2
3
4
5
6
7
inline fun<reified T> showMessage(marks: Int): T {
return when (T::class) {
Int::class -> marks as T
String::class -> "Congratulations! you scored more than 90%" as T
else -> "Please enter valid type" as T
}
}
Или для замены ссылок на классы более лаконичным кодом.
1
2
3
4
5
6
7
8
inline fun <reified Т : Activity> Context.startActivity() {
val intent = Intent(this, Т::class.java)
startActivity(intent)
}
...
startActivity<DetailActivity>()
Встроенная функция с
reified
не может быть вызвана из кода Java. Такие функции требуют дополнительной обработки для подстановки значения типовых аргументов в байт-код, и поэтому всегда должны быть встраиваемыми. А inline-функции можно вызвать из Java только как функции без встраивания.
Почему reified
возможно использовать только с встроенными функциями?
Когда мы вызываем inline-функцию, у которой параметр отмечен ключевым словом reified
, компилятор определяет тип этого параметра и генерирует байт-код, который ссылается на конкретный класс. А так как в байт-коде указан конкретный класс, а не типовой параметр, типовой аргумент не стирается во время выполнения. Этот байт-код в последующем будет вставляться в точки вызова inline-функции.
Полезные ссылки
Inline Functions - официальная документация.
Встроенные (inline) функции - неофициальный перевод на русский язык.