Что такое лямбда функция kotlin
В Kotlin существует модификатор inline , которым можно пометить функцию. Основное его предназначение - повысить производительность. Чтобы понять за счёт чего она повышается, нужно вспомнить лямбда-выражения.
Как правило, лямбда-выражения компилируются в анонимные классы. То есть каждый раз, когда используется лямбда-выражение, создаётся дополнительный класс. Отсюда вытекают дополнительные накладные расходы у функций, которые принимают лямбду в качестве аргумента. Если же функцию отметить модификатором inline , то компилятор не будет создавать анонимные классы и их объекты для каждого лямбда-выражения, а просто вставит код её реализации в место вызова. Или другими словами встроит её.
Модификатор inline влияет и на функцию, и на лямбду, переданную ей: они обе будут встроены в место вызова.
Если же вы хотите, чтобы некоторые лямбды, переданные inline-функции, не были встроены, то отметьте их модификатором noinline .
Разница между ними в том, что встраиваемая лямбда может быть вызвана только внутри inline-функции, либо может быть передана в качестве встраиваемого аргумента. В то время как noinline-лямбды можно хранить внутри полей, передавать куда-либо итд.
Return
Иначе работает и оператор return . В лямбда-выражении оператор return завершает работу всей функции, в которой было вызвано лямбда-выражение. Для выхода только из лямбды используется label .
Но если лямбда-выражение передаётся невстраиваемой функции, то использование оператора return недопустимо. Если же лямбда-выражение передаётся в inline-функцию, то оператор return разрешён и он завершает работу этой функции.
Reified
Reified - это ключевое слово, которое может быть использовано только в inline-функциях. Его цель - получение доступа к информации о типе класса.
Параметры, отмеченные этим ключевым словом, ещё называют овеществляемыми.
Допустим у нас есть такая функция:
Она универсальная и может быть использована для любого типа переменной. Такой эффект достигнут благодаря использованию дженериков (обобщения). Если мы захотим узнать тип класса T ,
то получим ошибку Cannot use 'T' as reified type parameter , так как информация о типе в дженериках отсутствует.
Тут нам на помощь и приходит reified . Ключевое слово указывается перед типом, информацию о котором мы хотим получить внутри функции.
За кулисами компилятор заменит тип T на фактический, поэтому мы сможем получить о нём информацию и при этом нет необходимости этот тип явно передавать функции.
Также reified может быть использован в другом сценарии: для возврата из функции разных типов данных.
Или для замены ссылок на классы более лаконичным кодом.
Встроенная функция с reified не может быть вызвана из кода Java. Такие функции требуют дополнительной обработки для подстановки значения типовых аргументов в байт-код, и поэтому всегда должны быть встраиваемыми. А inline-функции можно вызвать из Java только как функции без встраивания.
Почему reified возможно использовать только с встроенными функциями?
Когда мы вызываем inline-функцию, у которой параметр отмечен ключевым словом reified , компилятор определяет тип этого параметра и генерирует байт-код, который ссылается на конкретный класс. А так как в байт-коде указан конкретный класс, а не типовой параметр, типовой аргумент не стирается во время выполнения. Этот байт-код в последующем будет вставляться в точки вызова inline-функции.
Полезные ссылки
Inline Functions - официальная документация.
Встроенные (inline) функции - неофициальный перевод на русский язык.
В Kotlin функции являются функциями первого класса. Это значит, что они могут храниться в переменных и структурах данных, передаваться в качестве аргументов и возвращаться из других функций высшего порядка. Вы можете работать с функциями любым способом, который возможен для других нефункциональных значений.
Чтобы это облегчить, Kotlin, как статически типизированный язык программирования, использует семейство функциональных типов для представления функций и предоставляет набор специализированных языковых конструкций, таких как лямбда-выражения.
Функции высшего порядка
Функция высшего порядка - это функция, которая принимает функции как параметры, или возвращает функцию в качестве результата.
Хорошим примером такой функции является идиома функционального программирования fold для коллекций, которая принимает начальное значение - accumulator вместе с комбинирующей функцией и строит возвращаемое значение, последовательно комбинируя текущее значение accumulator с каждым элементом коллекции, заменяя значение accumulator .
В приведённом выше коде параметр combine имеет функциональный тип (R, T) -> R , поэтому он принимает функцию, которая принимает два аргумента типа R и T и возвращает значение типа R . Он вызывается внутри цикла for и присваивает accumulator возвращаемое значение.
Чтобы вызвать fold , вы должны передать ему экземпляр функционального типа в качестве аргумента и лямбда-выражение (описание ниже). Лямбда-выражения часто используются в качестве параметра функции высшего порядка.
Функциональные типы
Kotlin использует семейство функциональных типов, таких как (Int) -> String , для объявлений, которые являются частью функций: val onClick: () -> Unit = . .
Эти типы имеют специальные обозначения, которые соответствуют сигнатурам функций, то есть их параметрам и возвращаемым значениям:
- У всех функциональных типов есть список с типами параметров, заключенный в скобки, и возвращаемый тип: (A, B) -> C обозначает тип, который предоставляет функции два принятых аргумента типа A и B , а также возвращает значение типа C . Список с типами параметров может быть пустым, как, например, в () -> A . Возвращаемый тип Unit не может быть опущен;
- У функциональных типов может быть дополнительный тип - получатель (ориг.: receiver), который указывается в объявлении перед точкой: тип A.(B) -> C описывает функции, которые могут быть вызваны для объекта-получателя A с параметром B и возвращаемым значением C . Литералы функций с объектом-приёмником часто используются вместе с этими типами;
-
(ориг.: suspending functions) принадлежат к особому виду функциональных типов, у которых в объявлении присутствует модификатор suspend , например, suspend () -> Unit или suspend A.(B) -> C .
Объявление функционального типа также может включать именованные параметры: (x: Int, y: Int) -> Point . Именованные параметры могут быть использованы для описания смысла каждого из параметров.
Чтобы указать, что функциональный тип может быть nullable, используйте круглые скобки: ((Int, Int) -> Int)? .
При помощи круглых скобок функциональные типы можно объединять: (Int) -> ((Int) -> Unit) .
The arrow notation is right-associative, `(Int) -> (Int) -> Unit` is equivalent to the previous example, but not to `((Int) -> (Int)) -> Unit`. -->
Стрелка в объявлении является правоассоциативной (ориг.: right-associative), т.е. объявление (Int) -> (Int) -> Unit эквивалентно объявлению из предыдущего примера, а не ((Int) -> (Int)) -> Unit .
Вы также можете присвоить функциональному типу альтернативное имя, используя псевдонимы типов.
Создание функционального типа
Существует несколько способов получить экземпляр функционального типа:
Используя блок с кодом внутри функционального литерала в одной из форм:
Литералы функций с объектом-приёмником могут использоваться как значения функциональных типов с получателем.
Используя вызываемую ссылку на существующее объявление:
- функции верхнего уровня, локальной функции, функции-члена или функции-расширения: ::isOdd , String::toInt ,
- свойства верхнего уровня, члена или свойства-расширения: List::size , : ::Regex
К ним относятся привязанные вызываемые ссылки, которые указывают на член конкретного экземпляра: foo::toString .
- Используя экземпляр пользовательского класса, который реализует функциональный тип в качестве интерфейса:
При достаточной информации компилятор может самостоятельно вывести функциональный тип для переменной.
C` can be passed or assigned where a value of type `A.(B) -> C` is expected, and the other way around: -->
Небуквальные (ориг.: non-literal) значения функциональных типов с и без получателя являются взаимозаменяемыми, поэтому получатель может заменить первый параметр, и наоборот. Например, значение типа (A, B) -> C может быть передано или назначено там, где ожидается A.(B) -> C , и наоборот.
A function type with no receiver is inferred by default, even if a variable is initialized with a reference > to an extension function. > To alter that, specify the variable type explicitly. -->
Обратите внимание, что функциональный тип без получателя выводится по умолчанию, даже если переменная инициализируется со ссылкой на функцию-расширение. Чтобы это изменить, укажите тип переменной явно.
Вызов экземпляра функционального типа
Значение функционального типа может быть вызвано с помощью оператора invoke(. ) : f.invoke(x) или просто f(x) .
Если значение имеет тип получателя, то объект-приёмник должен быть передан в качестве первого аргумента. Другой способ вызвать значение функционального типа с получателем - это добавить его к объекту-приёмнику, как если бы это была функция-расширение: 1.foo(2) .
Встроенные функции
Иногда выгодно улучшить производительность функций высшего порядка, используя встроенные функции (ориг.: inline functions).
Лямбда-выражения и анонимные функции
Лямбда-выражения и анонимные функции - это "функциональный литерал", то есть необъявленная функция, которая немедленно используется в качестве выражения. Рассмотрим следующий пример:
Функция max является функцией высшего порядка, потому что она принимает функцию в качестве второго аргумента. Этот второй аргумент является выражением, которое в свою очередь есть функция, то есть функциональный литерал. Как функция он эквивалентен объявлению:
Синтаксис лямбда-выражений
Полная синтаксическая форма лямбда-выражений может быть представлена следующим образом:
- Лямбда-выражение всегда заключено в скобки ;
- Объявление параметров при таком синтаксисе происходит внутри этих скобок и может включать в себя аннотации типов;
- Тело функции начинается после знака -> ;
- Если тип возвращаемого значения не Unit , то в качестве возвращаемого типа принимается последнее (а возможно и единственное) выражение внутри тела лямбды.
Если вы вынесите все необязательные объявления, то, что останется, будет выглядеть следующим образом:
Передача лямбды в качестве последнего параметра
В Kotlin существует соглашение: если последний параметр функции является функцией, то лямбда-выражение, переданное в качестве соответствующего аргумента, может быть вынесено за круглые скобки.
Такой синтаксис также известен как trailing lambda.
Когда лямбда-выражение является единственным аргументом функции, круглые скобки могут быть опущены.
it: неявное имя единственного параметра
Очень часто лямбда-выражение имеет только один параметр.
` can be omitted. The parameter will be implicitly declared under the name `it`: -->
Если компилятор способен самостоятельно определить сигнатуру, то объявление параметра можно опустить вместе с -> . Параметр будет неявно объявлен под именем it .
Возвращение значения из лямбда-выражения
Вы можете вернуть значение из лямбды явно, используя оператор return. Либо неявно будет возвращено значение последнего выражения.
Таким образом, два следующих фрагмента равнозначны:
Это соглашение, вместе с передачей лямбда-выражения вне скобок, позволяет писать код в стиле LINQ.
Символ подчеркивания для неиспользуемых переменных
Если параметр лямбды не используется, то разрешено его имя заменить на символ подчёркивания.
Деструктуризация в лямбдах
Деструктуризация в лямбдах описана в Деструктурирующие объявления.
Анонимные функции
Единственной особенностью синтаксиса лямбда-выражений, о которой ещё не было сказано, является способность определять и назначать возвращаемый функцией тип. В большинстве случаев в этом нет особой необходимости, потому что он может быть вычислен автоматически. Однако, если у вас есть потребность в определении возвращаемого типа, вы можете воспользоваться альтернативным синтаксисом: анонимной функцией.
Объявление анонимной функции выглядит очень похоже на обычное объявление функции, за исключением того, что её имя опущено. Тело такой функции может быть описано и выражением (как показано выше), и блоком.
Параметры функции и возвращаемый тип обозначаются таким же образом, как в обычных функциях. за исключением того, что тип параметра может быть опущен, если его значение следует из контекста.
Аналогично и с типом возвращаемого значения: он вычисляется автоматически для функций-выражений или же должен быть явно определён (если не является типом Unit ) для анонимных функций с блоком в качетсве тела.
When passing anonymous functions as parameters, place them inside the parentheses. The shorthand syntax that allows you to leave > the function outside the parentheses works only for lambda expressions. -->
Обратите внимание, что параметры анонимных функций всегда заключены в круглые скобки (. ) . Приём, позволяющий оставлять параметры вне скобок, работает только с лямбда-выражениями.
Одним из отличий лямбда-выражений от анонимных функций является поведение оператора return (non-local returns). Слово return , не имеющее метки ( @ ), всегда возвращается из функции, объявленной ключевым словом fun . Это означает, что return внутри лямбда-выражения возвратит выполнение к функции, включающей в себя это лямбда-выражение. Внутри анонимных функций оператор return , в свою очередь, выйдет, собственно, из анонимной функции.
Замыкания
Лямбда-выражение или анонимная функция (так же, как и локальная функция или анонимные объекты) имеет доступ к своему замыканию, то есть к переменным, объявленным вне этого выражения или функции. Переменные, захваченные в замыкании, могут быть изменены в лямбде.
Литералы функций с объектом-приёмником
C`, can be instantiated with a special form of function literals – function literals with receiver. -->
Функциональные типы с получателем, такие как A.(B) -> C , могут быть вызваны с помощью особой формы – литералов функций с объектом-приёмником.
Как было сказано выше, Kotlin позволяет вызывать экземпляр функционального типа с получателем, предоставляющим объект-приёмник.
Внутри тела литерала объект-приёмник, переданный при вызове функции, становится неявным this , поэтому вы можете получить доступ к членам этого объекта-приёмника без каких-либо дополнительных определителей, а обращение к самому объекту-приёмнику осуществляется с помощью выражения this .
Это схоже с принципом работы функций-расширений, которые позволяют получить доступ к членам объекта-приёмника внутри тела функции.
Ниже приведён пример литерала с получателем вместе с его типом, где plus вызывается для объекта-приёмника:
Синтаксис анонимной функции позволяет вам явно указать тип приёмника. Это может быть полезно в случае, если вам нужно объявить переменную типа нашей функции для использования в дальнейшем.
Лямбда-выражения могут быть использованы как литералы функций с приёмником, когда тип приёмника может быть выведен из контекста. Один из самых важных примеров их использования это типобезопасные строители (ориг.: type-safe builders).
Стандартная библиотека Kotlin содержит несколько функций, единственной целью которых является выполнение блока кода в контексте объекта. Эти функции формируют временную область видимости для объекта, к которому были применены, и вызывают код, указанный в переданном лямбда-выражении. В этой области видимости можно получить доступ к объекту без явного к нему обращения по имени. Такие функции называются функциями области видимости (англ. scope functions). Всего их пять: let , run , with , apply , и also .
По сути, все эти функции делают одно и то же: выполняют блок кода для объекта. Отличие состоит в том, как этот объект становится доступным внутри блока и каков результат всего выражения.
Пример обычного использования функции области видимости:
Если вы захотите написать то же самое без функции let , то вам придется объявить новую переменную и обращаться к ней всякий раз, когда она используется.
Функции области видимости не предоставляют никаких новых технических возможностей, но они могут сделать ваш код более кратким и читабельным.
Из-за того, что все функции области видимости имеют схожий характер, выбрать правильную для вашего конкретного случая может быть затруднительно. В основном выбор зависит от ваших намерений и от структуры вашего проекта. Ниже приведено подробное описание того, чем функции области видимости отличаются между собой, а также соглашение об их использовании.
Отличительные особенности
Поскольку функции области видимости очень похожи друг на друга, важно понимать чем они различаются. Между ними есть два основных различия:
- Способ ссылки на контекстный объект
- Возвращаемое значение.
Контекстный объект: this или it
Внутри лямбда-выражения, которое передается функции области видимости, объект контекста доступен по краткой ссылке, а не по его фактическому имени. Каждая функция области видимости использует один из двух способов доступа к объекту контекста: как лямбда-получатель ( this ) или как лямбда-аргумент ( it ). Оба предоставляют одинаковые возможности, поэтому опишем плюсы и минусы каждого для разных случаев и дадим рекомендации по их использованию.
run , with и apply ссылаются на объект контекста как лямбда-получатель - по ключевому слову this . Следовательно, в их лямбдах объект доступен, как это было бы в обычных функциях класса. В большинстве случаев this можно опустить при доступе к элементам объекта-получателя, что сделает код короче. С другой стороны, если this опущено, то будет сложнее различить элементы-получатели с внешними объектами или функциями. Таким образом, наличие this рекомендуется для лямбд, которые в основном работают с членами объекта: вызывают его функции или присваивают свойства.
В свою очередь, let и also передают контекстный объект как аргумент в лямбду. Если имя аргумента не указано, то к объекту обращаются неявным способом при помощи ключевого слова it . it короче this и выражения с it более читабельны. Однако при вызове функций или свойств объекта у вас не будет доступа к такому неявному объекту как this . Следовательно, использовать контекстный объект it лучше, когда объект в основном используется в качестве аргумента для вызова функций. it также лучше, если вы используете несколько переменных в блоке кода.
Кроме того, когда вы передаете объект в качестве аргумента, вы можете указать пользовательское имя для этого объекта внутри области видимости.
Возвращаемое значение
Функции области видимости также отличаются по значению, которое они возвращают:
- apply и also возвращают объект контекста.
- let , run и with возвращают результат лямбды.
Это отличие позволит выбрать правильную функцию в зависимости от того, что вы будете делать дальше в своем коде.
Контекстный объект
Функции apply и also возвращают объект контекста. Следовательно, с их помощью можно вызвать длинную цепочку функций относительно оригинального контекстного объекта. Такая цепочка функций известна как side steps.
Они также могут быть использованы совместно с ключевым словом return .
Результат лямбды
let , run и with возвращают результат лямбды. Таким образом, вы можете использовать их при присваивании переменной результата вычислений, либо использовать результат для последующего вызова цепочки операций и тд.
Кроме того, вы можете игнорировать возвращаемое значение и использовать функцию для создания временной области видимости для переменной.
Функции
Чтобы помочь вам выбрать правильную функцию области видимости, ниже будет представлено их подробное описание и рекомендации по использованию. Во многих случаях они взаимозаменяемы, поэтому в примерах будет отражен общий стиль использования, а также соглашение по их применению.
Контекстный объект доступен в качестве аргумента ( it ). Возвращаемое значение - результат выполнения лямбды.
Если значение переменной вычислялось при помощи цепочки операций, то let позволяет использовать полученный результат для вызова одной или нескольких функций в блоке кода. Например, в следующем коде выполняется цепочка из двух операций, результат записывается в отдельную переменную, после чего она выводится на печать.
С функцией let этот код может быть переписан следующим образом:
Если блок кода содержит одну функцию, где it является аргументом, то лямбда-выражение может быть заменено ссылкой на метод ( :: ):
let часто используется для выполнения блока кода только с non-null значениями. Чтобы выполнить действия с non-null объектом, используйте оператор безопасного вызова ?. совместно с функцией let .
Еще один вариант использования let - это введение локальных переменных с ограниченной областью видимости для улучшения читабельности кода. Чтобы определить новую переменную для контекстного объекта, укажите ее имя в качестве аргумента лямбды, чтобы ее можно было использовать вместо ключевого слова it .
Не является функцией-расширением. Контекстный объект передается в качестве аргумента, а внутри лямбда-выражения он доступен как получатель ( this ). Возвращаемое значение - результат выполнения лямбды.
Функцию with рекомендуется использовать для вызова функций контекстного объекта без предоставления результата лямбды. В коде with может читаться как" с этим объектом, сделайте следующее. "
Другой вариант использования with - введение вспомогательного объекта, свойства или функции которые будут использоваться для вычисления значения.
Контекстный объект доступен в качестве получателя ( this ). Возвращаемое значение - результат выполнения лямбды.
run делает то же самое, что и with , но вызывается как let - как функция расширения контекстного объекта.
run удобен, когда лямбда содержит и инициализацию объекта, и вычисление возвращаемого значения.
Помимо вызова run для объекта-получателя, вы можете использовать его как функцию без расширения. В этом случае run позволяет выполнить блок из нескольких операторов там, где это требуется.
apply
Контекстный объект доступен в качестве получателя ( this ). Возвращаемое значение - контекстный объект.
Используйте apply для такого блока кода, который не возвращает значение и в основном работает с членами объекта-получателя. Типичный способ использования функции apply - настройка объекта-получателя. Это всеравно что мы скажем “примени перечисленные настройки к объекту.”
Так как возвращаемое значение - это сам объект, то можно с легкостью включить apply в цепочки вызовов для более сложной обработки.
Контекстный объект доступен в качестве аргумента ( it ). Возвращаемое значение - контекстный объект.
also хорош для выполнения таких действий, которые принимают контекстный объект в качестве аргумента. То есть, эту функции следует использовать, когда требуется ссылка именно на объект, а не на его свойства и функции. Либо, когда вы хотите, чтобы была доступна ссылка на this из внешней области видимости.
Когда вы видите в коде also , то это можно прочитать как "а также с объектом нужно сделать следующее."
Выбор функции
В таблице ниже приведены ключевые различия между функциями области видимости, что должно помочь вам сделать правильный выбор в пользу той или иной функции.
Функция | Обращение к объекту | Возвращаемое значение | Является функцией-расширением |
---|---|---|---|
let | it | Результат лямбды | Да |
run | this | Результат лямбды | Да |
run | - | Результат лямбды | Нет: может быть вызвана без контекстного объекта |
with | this | Результат лямбды | Нет: принимает контекстный объект в качестве аргумента. |
apply | this | Контекстный объект | Да |
also | it | Контекстный объект | Да |
Краткое руководство по выбору функции области видимости в зависимости от предполагаемого назначения:
- Выполнение лямбды для non-null объектов: let
- Представление переменной в виде выражения со своей локальной областью видимости: let
- Настройка объекта: apply
- Настройка объекта и вычисление результата: run
- Выполнение операций, для которых требуется выражение: run без расширения
- Применение дополнительных значений: also
- Группировка всех функций, вызываемых для объекта: with
Некоторые функции области видимости являются взаимозаменяемыми, поэтому вы можете выбирать функции исходя из соглашений, принятых в вашем проекте или команде.
Несмотря на то, что функции области видимости предназначены для того, чтобы сделать код более кратким, избегайте их чрезмерного использования: это может снизить читабельность кода и привести к ошибкам. Избегайте вложенности функций и будьте осторожны при их объединении: можно легко запутаться в текущем значении контекстного объекта и в значениях this или it .
takeIf и takeUnless
Помимо функций области видимости, стандартная библиотека содержит функции takeIf и takeUnless . Эти функции позволяют встроить проверку состояния объекта в цепочке вызовов.
При вызове takeIf для объекта с предикатом этот объект будет возвращен, если он соответствует предикату. В противном случае возвращается null . В свою очередь, takeUnless возвращает объект, если он не соответствует предикату, и null , если соответствует. Объект доступен как лямбда-аргумент ( it ).
При добавлении в цепочку вызовов других функций после takeIf и takeUnless , не забудьте выполнить проверку на null или используйте оператор безопасного вызова ( ?. ), потому что их возвращаемое значение имеет тип nullable.
takeIf и takeUnless особенно полезны при совместном использовании с функциями области видимости. Хорошим примером является объединение их в цепочку с let для выполнения блока кода для объектов, которые соответствуют заданному предикату. Для этого вызовите takeIf для объекта, а затем вызовите let с оператором безопасного вызова ( ? ). Для объектов, которые не соответствуют предикату, takeIf возвращает null , а let не вызывается.
Тот же самый код, но без использования функций из стандартной библиотеки, выглядит следующим образом:
Лямбда-выражения и анонимные функции - это функции без имени, которые могут быть переданы в качестве аргумента другим функциям. Их можно объявлять отдельно: сохранить в переменной и вызывать в нужном месте. Но обычно в этом нет необходимости, поэтому чаще всего они объявляются непосредственно при передаче в функцию.
Как правило они используются совместно с функциями из стандартной библиотеки Kotlin: с их помощью можно настроить поведение стандартных функций и добавить для них правила.
Синтаксис
Лямбда-выражения
Лямбда-выражения всегда окружены фигурными скобками. Список аргументов отделяется от тела лямбды стрелкой -> .
Параметров может быть несколько, как в примере выше, либо может вовсе не быть, тогда левая часть вместе со стрелкой -> опускаются.
Если в лямбда-выражении только один параметр, при этом его тип может быть выведен автоматически, то объявление параметра можно опустить вместе с -> . В данном случае для обращения к параметру будет создано имя по умолчанию - it .
Значение из лямбда-выражения можно вернуть явно - при помощи оператора return . Либо неявно будет возвращено значение последнего (а возможно и единственного) выражения.
Если лямбда-выражение является последним аргументом функции, то оно может быть вынесено за круглые скобки. Если же оно является единственным аргументом функции, то круглые скобки можно вовсе опустить. Такой синтаксис также известен как trailing lambda.
Может встретиться такое, что один из параметров лямбда-выражения не используется. Имя такого параметра можно заменить на символ подчёркивания _ . Kotlin умеет отслеживать неиспользуемые параметры, поэтому будет в коде их все выделять и советовать переименовать.
Если лямбда-выражение определено в функции, то оно может обращаться к её параметрам и локальным переменным, объявленным перед лямбда-выражением.
Анонимные функции
Анонимная функция объявляется также как и обычная функция, но без указания имени.
В отличии от обычных функций, в анонимной функции можно опустить тип параметров, если его можно вывести из контекста.
Анонимные функции следуют тем же правилам, что и обычные функции: для функций с телом-выражением возвращаемый тип и оператор return могут быть опущены, но для функций с телом-блоком они должны быть указаны явно (как в примере выше).
Разница между лямбда-выражением и анонимной функцией
Мне, как новичку, было сложно понять разницу между этими понятиями. В результате я для себя выделила следующее:
Изначально появилось понятие анонимной функции как функции, у которой нет имени. Такие функции создаются в месте использования, либо присваиваются переменной и потом в нужном месте косвенно вызываются. Хотя в последнем случае функция всё таки получает имя за счет переменной и перестаёт быть анонимной, но тем не менее такой пример использования анонимной функции встречается повсеместно.
Лямбда-выражение в общепринятом смысле - это синтаксическая конструкция для объявления анонимной функции. То есть по сути оно позволяет объявлять анонимную функцию в более коротком и читабельном виде.
В Kotlin же этим понятиям дали несколько иной смысл, по сути поменяли местами. Под лямбда-выражением понимается небольшой фрагмент кода, который можно передать другой функции. При этом лямбды чаще всего встречаются и применяются. Анонимная же функция - это другая синтаксическая форма лямбда-выражения, которую добавили для более удобной реализации следующих случаев (они же являются и различиями между лямбдой и анонимной функцией):
Необходимость в определении возвращаемого типа. Лямбда-выражение выводит тип возвращаемого значения самостоятельно, а в анонимной функции его можно задать явно. Данный случай использования анонимной функции описан в официальной документации языка.
Второй случай связан с оператором return . В лямбда-выражении оператор return производит выход из функции, в которой вызывается это лямбда-выражение (т.е. полностью завершает работу этой функции и код, указанный после оператора return никогда не выполнится). Чтобы завершить работу только лямбда-выражения, к оператору return должна быть добавлена метка, но такой синтаксис считается неуклюжим, потому что может запутать при наличии нескольких выражений return .
В анонимной же функции оператор return завершает работу самой анонимной функции. К тому же её намного удобнее использовать, когда требуется написать блок кода с несколькими точками выхода. Данное отличие описано в официальной документации, но более понятно объяснено на stackoverflow.
Читайте также: