← Назад к вопросам
Для чего нужно ключевое слово crossinline в inline функции?
2.0 Middle🔥 151 комментариев
#Kotlin основы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Назначение crossinline в Kotlin
crossinline — это модификатор для параметров inline функций, который позволяет передавать лямбду в другие функции (обычно в nested функции или корутины), не позволяя ей использовать return для выхода из внешней функции.
Основная проблема
В обычной inline функции return выходит из ВНЕШНЕЙ функции (non-local return):
inline fun forEach(action: (Int) -> Unit) {
for (i in 1..3) {
action(i)
}
}
fun test() {
forEach { i ->
if (i == 2) return // Выходит из test(), не из forEach!
}
println("Это не выпечатается")
}
Что делает crossinline?
crossinline запрещает использование return в лямбде, позволяя её передавать в nested функции:
inline fun runAsync(
crossinline block: suspend () -> Unit
) {
GlobalScope.launch {
block() // Передаём в suspend функцию
}
}
fun test() {
runAsync {
// ❌ return здесь запрещён (нарушает crossinline)
delay(1000)
println("Done")
}
}
Зачем нужен crossinline?
1. Использование лямбды в suspend функциях
Suspend функции не могут содержать non-local return:
inline fun loadDataAsync(
crossinline onSuccess: (String) -> Unit,
crossinline onError: (Exception) -> Unit
) {
GlobalScope.launch {
try {
val data = fetchData() // suspend
onSuccess(data)
} catch (e: Exception) {
onError(e)
}
}
}
fun test() {
loadDataAsync(
onSuccess = { data -> println(data) },
onError = { e -> println(e.message) }
)
}
2. Передача лямбды в другие функции
inline fun withLock(
crossinline block: () -> Unit
) {
val lock = ReentrantLock()
lock.withLock {
block() // Передаём в withLock
}
}
fun test() {
withLock {
println("Protected code")
// ❌ return не допускается
}
}
3. Использование в Thread
inline fun runInThread(
crossinline action: () -> Unit
) {
Thread {
action() // Передаём в конструктор Thread
}.start()
}
fun test() {
runInThread {
Thread.sleep(1000)
println("In thread")
// ❌ return запрещён
}
}
Inline vs Noinline vs Crossinline
inline fun example(
block1: (Int) -> Unit, // inline: может быть встроена
noinline block2: (Int) -> Unit, // noinline: может быть сохранена
crossinline block3: (Int) -> Unit // crossinline: может быть передана дальше
) {
block1(1) // Встраивается, return работает
block2(2) // Сохраняется, return работает
launch { // Suspend функция
block3(3) // Передаётся внутрь, return запрещён
}
}
Практические примеры
1. Работа с Room DAO (асинхронный результат)
inline fun <T> withDatabase(
database: AppDatabase,
crossinline block: suspend (AppDatabase) -> T
) {
viewModelScope.launch {
try {
val result = block(database)
// Обработка результата
} catch (e: Exception) {
// Обработка ошибки
}
}
}
// Использование
withDatabase(database) { db ->
db.userDao().getUsers()
}
2. Retry с задержкой
inline fun retryWithDelay(
maxAttempts: Int = 3,
delay: Long = 1000,
crossinline block: suspend () -> Unit
) {
viewModelScope.launch {
repeat(maxAttempts) { attempt ->
try {
block()
return@launch // Успех
} catch (e: Exception) {
if (attempt == maxAttempts - 1) throw e
delay(delay)
}
}
}
}
// Использование
retryWithDelay {
api.fetchData()
}
3. Обработка Event с корутинами
inline fun observeEvent(
flow: Flow<Event>,
crossinline handler: suspend (Event) -> Unit
) {
viewModelScope.launch {
flow.collect { event ->
handler(event)
}
}
}
fun test() {
observeEvent(eventFlow) { event ->
println(event)
// ❌ return запрещён, используй return@observeEvent
}
}
Почему нельзя просто использовать return?
inline fun problem(
block: () -> Unit // БЕЗ crossinline
) {
Thread {
block() // ❌ ОШИБКА: return будет выходить из problem(),
// но мы уже в другом потоке!
}.start()
}
Таблица модификаторов
| Модификатор | Встраивается | Сохраняется | Передаётся | return работает |
|---|---|---|---|---|
| Обычный | ✅ Да | ❌ Нет | ❌ Нет | ✅ Да (non-local) |
| noinline | ❌ Нет | ✅ Да | ✅ Да | ✅ Да |
| crossinline | ✅ Да | ❌ Нет | ✅ Да | ❌ Нет |
Итог
crossinline нужен когда:
- Лямбда передаётся в suspend функцию
- Лямбда передаётся в Thread или другой контекст выполнения
- Нужно встраить лямбду, но запретить non-local return
- Используется с GlobalScope.launch, viewModelScope.launch и т.д.
Правило:
- inline = встраивание + non-local return
- noinline = сохранение + non-local return
- crossinline = встраивание + БЕЗ non-local return