Intereting Posts
Как написать рекурсивные сопрограммы в котлин android studio 3.0.1 и kotlin v1.2.10-release-Studio3.0-1 получили проблемы Тип несоответствия: выведенный тип – String? но String ожидалось в kotlin TornadoFX – Создание MVP-дизайна метод getter бросает StackOverflowError POSTING для API сервера с Kotlin (OkHttp / Retrofit) recyclerView с Kotlin в новом классе Kotlin: как параметр по умолчанию в «fun main (parameters: Array <String>)» печатает «гость» без присвоения каких-либо значений Текстовый объект объекта Kotlin, реализующий дикий кардочесальный, саморегуляторный общий интерфейс Вопрос о начальном и вторичном конструкторе Котлина Как смешивать несколько конструкторов родительского класса с val в дочернем классе kotlin-android null не может быть отличен для непустого типа kotlin.String RecyclerView – правильно внедрить удаление UNDO SnackBar (Kotlin) Создание runnable / uber jar с maven из проекта, смешанного с Java и Kotlin Отладчик Android Studio не работает при использовании поддержки на C ++

В чем разница между crossinline и noinline в Котлине?

  1. Этот код компилируется с предупреждением ( незначительное влияние на производительность ):

    inline fun test(noinline f: () -> Unit) { thread(block = f) } 
  2. Этот код не компилируется ( незаконное использование встроенного параметра ):

     inline fun test(crossinline f: () -> Unit) { thread(block = f) } 
  3. Этот код компилируется с предупреждением ( незначительное влияние на производительность ):

     inline fun test(noinline f: () -> Unit) { thread { f() } } 
  4. Этот код компилируется без предупреждения или ошибки :

     inline fun test(crossinline f: () -> Unit) { thread { f() } } 

Вот мои вопросы:

  • Почему (2) не компилируется, но (4) делает?
  • В чем же разница между noinline и crossinline ?
  • Если (3) не генерирует никаких улучшений производительности, зачем делать (4)?

Solutions Collecting From Web of "В чем разница между crossinline и noinline в Котлине?"

Из ссылки встроенных функций :

Обратите внимание, что некоторые встроенные функции могут вызывать lambdas, переданные им как параметры не непосредственно из тела функции, а из другого контекста выполнения, такого как локальный объект или вложенная функция. В таких случаях нелокальный поток управления также не допускается в лямбдах. Чтобы указать, что параметр лямбда должен быть отмечен модификатором кросс-линии

Следовательно, пример 2. не компилируется, поскольку crossinline обеспечивает только локальный поток управления, а block = f expression block = f нарушает это. Пример 1 компилируется, поскольку noinline не требует такого поведения (очевидно, так как это обычный параметр функции).

Примеры 1 и 3 не генерируют никаких улучшений производительности, поскольку единственный параметр лямбда отмечен noinline , что делает inline модификатор функции бесполезным и избыточным – компилятор хотел бы встроить что-то, но все, что могло быть отмечено, не было встраиваемый.

Рассмотрим две функции: А и В

 inline fun test(noinline f: () -> Unit) { thread { f() } } 

В

 fun test(f: () -> Unit) { thread { f() } } 

Функция A ведет себя как функция B в том смысле, что параметр f не будет проиндексирован (функция B не строит тело test тогда как в функции A тело: thread { f() } все равно встает в очередь).

Теперь это неверно в примере 4, поскольку параметр crossinline f: () -> Unit может быть встроен, он просто не может нарушить вышеупомянутое правило нелокального управления потоком (например, присвоение нового значения глобальной переменной). И если он может быть встроен, компилятор предполагает повышение производительности и не предупреждает, как в примере 3.

Q1: Почему (2) не компилируется, но (4) делает?

Из их документа:

Встраиваемые лямбды могут быть вызваны только внутри встроенных функций или переданы как убедительные аргументы …

Ответ:

Метод thread(...) не является inline методом, поэтому вы не сможете передать f в качестве аргумента.

Q2: В чем же разница между noinline и crossinline?

Ответ:

noinline предотвратит встраивание лямбда. Это становится полезным, когда у вас есть несколько аргументов лямбда, и вы хотите, чтобы только некоторые из lambdas, переданные встроенной функции, были встроены.

crossinline используется для отметки lambdas, которая не должна допускать нелокальных возвратов, особенно когда такая lambda передается другому контексту выполнения. Другими словами, вы не сможете использовать return в таких лямбдах. Используя ваш пример:

 inline fun test(crossinline f: () -> Unit) { thread { f() } } //another method in the class fun foo() { test{ //Error! return is not allowed here. return } } 

Q3: Если (3) не создает никаких улучшений производительности, почему бы (4) сделать?

Ответ:

Это потому, что единственная лямбда, которую вы имеете в (3), была отмечена noinline что означает, что у вас будут накладные расходы на создание объекта Function для размещения тела вашей lamda. Для (4) лямбда все еще встроена (повышение производительности) только для того, чтобы она не позволяла нелокальные возвращения.

К первому и второму вопросу

Почему (2) не компилируется, но (4) делает? .. разница между noinline и crossinline

 2. inline fun test(crossinline f: () -> Unit) { thread(block = f) } 4. inline fun test(crossinline f: () -> Unit) { thread { f() } } 

Оба случая имеют inline модификатор, инструктирующий встраивать как функциональный test и его аргумент lambda f . Из ссылки kotlin:

Встроенный модификатор влияет как на саму функцию, так и на lambdas, переданную ей: все они будут включены в сайт вызова.

Поэтому компилятору предлагается поместить код (встроенный) вместо создания и вызова объекта функции для f . crossinline модификатор предназначен только для встроенных вещей: он просто говорит, что переданный лямбда (в параметре f ) не должен иметь нелокальных возвратов (которые могут иметь обычные «lambdas»). crossinline можно считать чем-то вроде этого (инструкция для компилятора): «сделайте inline, но есть ограничение, что оно пересекает контекст invoker и поэтому убедитесь, что лямбда не имеет нелокальных возвратов.

На стороне примечание, thread кажется концептуально иллюстративным примером для crossinline потому что, очевидно, возврат из некоторого кода (переданного в f ) позже в другом потоке не может повлиять на возврат из test , который продолжает выполняться в потоке вызывающего независимо от того, что он порождается ( f продолжает выполнять самостоятельно).

В случае №4 есть лямбда (фигурные скобки), вызывающие f() . В случае № 2 f передается непосредственно в качестве аргумента для thread

Поэтому в # 4 вызов f() может быть встроен, и компилятор может гарантировать отсутствие нелокального возврата. Чтобы разработать, компилятор заменит f() своим определением и затем будет «обернут» код внутри охватывающей лямбда, другими словами, { //code for f() } – это еще одна (обертка) лямбда, и сама она далее передается как ссылка объекта объекта (на thread ).

В случае № 2 ошибка компилятора просто говорит, что он не может встроить f потому что он передается как ссылка в «неизвестное» (не вложенное) место. crossinline становится неуместным и не имеет значения в этом случае, потому что он может применяться только в том случае, если f был встроен.

Подводя итог, случаи 2 и 4 не совпадают по сравнению с примером из ссылки котлина (см. «Функции более высокого порядка и Lambdas»): ниже вызывают эквиваленты, где фигурные скобки (лямбда-выражение) «заменяют» обертку функция toBeSynchronized

 //want to pass `sharedResource.operation()` to lock body fun <T> lock(lock: Lock, body: () -> T): T {...} //pass a function fun toBeSynchronized() = sharedResource.operation() val result = lock(lock, ::toBeSynchronized) //or pass a lambda expression val result = lock(lock, { sharedResource.operation() }) 

Варианты № 2 и №4 в вопросе не эквивалентны, потому что нет «обертки», вызывающей f в # 2