Лямбды
Лямбды всегда находятся внутри фигурных скобок. Слева находятся аргументы, справа — тело функции. Разделяет их специальное выражение ->. Например, создадим выражение.
x + 5>
Открыли фигурную скобку, записали параметр в виде числа Int, а затем указали, что с ним нужно делать в правой части — прибавить к числу 5. Параметр может быть один, как в нашем пример, несколько или вообще не быть.
Лямбда-выражение можно сохранить в переменной, а затем обращаться к ней как к обычной функции. Можно вызвать лямбда-выражение при помощи invoke().
val lambda = < println("Hello Kitty!") >lambda.invoke()
Лямбда-выражение может послужить удобной заменой для паттернов Listener или Callback.
Примеры
// Нет аргументов и возвращает 1 < 1 >// () -> Int // Один аргумент в виде строки, который выводится на экран < s: String ->println(s) > // (String)->Unit // два аргумента типа Int и возвращает произведение чисел < a: Int, b: Int ->a * b > // (Int, Int)->Int
Код с лямбдами становится короче и читабельнее.
button.setOnClickListener < /* код для щелчка кнопки */ >
val sum = < x: Int, y: Int ->x + y > println(sum(1, 2)) // 3
Лямбды можно записывать в несколько строк.
val test = < a: Int, b: Int ->println("$a + $b") a + b >
Этот же пример можно записать одной строкой, разделяя команды точкой с запятой.
val test = < a: Int, b: Int ->println(«$a + $b»); a + b >
setOnClickListener
Рассмотрим применение лямбда-выражений на примере обработчика щелчка кнопки.
Код на Java 7 и ниже.
button.setOnClickListener(new OnClickListener() < @Override public void onClick(View view) < /* код для щелчка кнопки */ >>);
Если использовать такой же код на Kotlin, то получим.
button.setOnClickListener(object : View.OnClickListener < override fun onClick(v: View?) < println("Hello Kitty") >>)
Студия предложит сконвертировать код, используя лямбду. В моём случае студия сразу сконвертировала в самый короткий вариант. Но мы рассмотрим все случаи, например, возможен такой вариант.
button.setOnClickListener( < v ->println("Hello Kitty") >)
Если в последнем параметре функции используется функция, то её можно вынести за круглые скобки. Получится следующее.
button.setOnClickListener() < v ->println("Hello Kitty") >
Если у функции несколько параметров, то только последнюю можно вынести за круглые скобки.
А мы продолжаем уменьшать код. Если у функции только один параметр и он является функцией, то круглые скобки можно убрать.
button.setOnClickListener < v ->println("Hello Kitty") >
Если в самом лямбда-выражении не используется параметр в левой части, то его можно удалить. Это справедливо для выражения с одним параметром. В нашем примере v (View) не используется, сокращаем код ещё раз.
button.setOnClickListener
Функция с одним параметром. Ключевое слово it
Рассмотрим частный случай, когда функция только получает параметр. Мы можем не использовать левую часть, а использовать ключевое слово it. Например, мы используем v для его передачи другой функции.
button.setOnClickListener < v ->doSomething(v) > private fun doSomething(v: View?)
Убираем левую часть и используем it.
button.setOnClickListener
Все варианты в одном месте.
button.setOnClickListener( < v ->println("Hello Kitty") >) button.setOnClickListener() < v ->println("Hello Kitty") > button.setOnClickListener < v ->println("Hello Kitty") > button.setOnClickListener < println("Hello Kitty") >button.setOnClickListener < v ->doSomething(v) > button.setOnClickListener
Другой пример использования — пройтись по элементам коллекции. Например, воспользуемся forEach:
val cats = listOf("Barsik", "Murzik", "Ryzhik") cats.forEach < println(it) >// Выводит Barsik Murzik Ryzhik
Без использования ключевого слова it пришлось бы писать длинный вариант.
cats.forEach < cat ->println(cat) >
Функции высшего порядка
Функция, использующая лямбда-выражение для параметра или возвращаемого значения, называется функцией высшего порядка.
Напишем функцию convert(), которая преобразует значение Double по формуле, передаваемой в лямбда-выражении, выводит результат и возвращает её. Например, с помощью этой функции вы сможете преобразовать температуру по Цельсию в температуру по Фаренгейту или преобразовать вес из килограммов в фунты — все зависит от формулы, которая передаётся в лямбда-выражении (аргумента). Начнём с определения параметров функции.
Для этого в функцию будут добавлены два параметра: Double и лямбда-выражение. Назовём лямбда-выражение converter, а поскольку оно будет использоваться для преобразования Double в Double, оно должно иметь тип (Double) -> Double (лямбда-выражение, которое получает параметр Double и возвращает Double).
fun convert(x: Double, converter: (Double) -> Double) : Double < // ваш код >
Напишем какой-то для функции.
fun convert(x: Double, converter: (Double) -> Double) : Double < val result = converter(x) println("$x is converted to $result") // выводим результат return result // вернуть результат >
Функция с лямбда-параметром вызывается точно так же, как и любая другая функция: с передачей значений всех аргументов. В данном случае Double и лямбда-выражений.
Давайте используем функцию convert() для преобразования 20 градусов по Цельсию в градусы по Фаренгейту. Для этого функции нужно передать значения 20.0 и лямбда-выражение < c: Double ->c * 1.8 + 32 >:
convert(20.0, < c: Double ->c * 1.8 + 32 >) // 20.0 is converted to 68.0 // с присвоением возвращаемого результата переменной val fahrenheit = convert(20.0, < c: Double ->c * 1.8 + 32 >) println(fahrenheit)
Если последний параметр вызываемой функции является лямбда-выражением, лямбда-аргумент можно вынести за круглые скобки вызова функции.
// было convert(20.0, < c: Double ->c * 1.8 + 32 >) // стало convert(20.0) < c: Double ->c * 1.8 + 32 >
Если функция имеет всего один параметр, и этот параметр представляет собой лямбда-выражение, круглые скобки при вызове функции можно полностью опустить.
Предположим, функция convertFive() преобразует Int 5 в Double по формуле преобразования, которая передаётся в виде лямбда-выражения.
fun convertFive(converter: (Int) -> Double) : Double
// стандартный вариант convertFive() < it * 1.8 + 32 >// компактный вариант без круглых скобок convertFive
Функция может возвращать лямбда-выражение. Например, следующий код определяет функцию с именем getConversionLambda(), которая возвращает лямбда-выражение типа (Double) -> Double. Точное лямбда-выражение, возвращаемое функцией, зависит от значения переданной строки.
fun getConversionLambda(str: String): (Double) -> Double < if (str == "CentigradeToFahrenheit") < return < it * 1.8 + 32 >> else if (str == "KgsToPounds") < return < it * 2.204623 >> else if (str == "PoundsToUSTons") < return < it / 2000.0 >> else < return < it >> >
Вы можете вызвать лямбда-выражение, возвращённое функцией, или использовать его в аргументе при вызове другой функции. Например, следующий код выполняет возвращаемое значение getConversionLambda для пересчета 2,5 кг в фунты и присваивает его переменной с именем pounds:
val pounds = getConversionLambda("KgsToPounds")(2.5) println(pounds)
Также возможно определить функцию, которая получает и возвращает лямбда-выражение.
Перегрузка операторов
Kotlin позволяет реализовывать предопределённый набор операторов для ваших типов. Эти операторы имеют фиксированное символическое представление (вроде + или * ) и фиксированные приоритеты. Для реализации оператора предоставьте функцию-член или функцию-расширение с фиксированным именем и с соответствующим типом, т. е. левосторонним типом для бинарных операций или типом аргумента для унарных операций.
Функции, которые перегружают операторы, должны быть отмечены модификатором operator .
interface IndexedContainer
При переопределении перегрузок оператора вы можете опускать operator .
class OrdersList: IndexedContainer < override fun get(index: Int) < /*. */ >>
Унарные операторы
Унарные префиксные операторы
| Выражение | Транслируется в |
|---|---|
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
Под транслированием, понимается представление вашего кода после компиляции.
Эта таблица демонстрирует, что компилятор при обрабатывании, к примеру, выражения +a , осуществляет следующие действия:
- Определяет тип выражения a , пусть это будет T ;
- Ищет функцию unaryPlus() с модификатором operator без параметров для приёмника типа Т , т. е. функцию-член или функцию-расширение;
- Если функция отсутствует или неоднозначная, возвращается ошибка компиляции;
- Если функция присутствует и R — её возвращаемый тип, выражение +a имеет Тип R .
Эти операции, как и все остальные, оптимизированы для основных типов и не требуют дополнительных затрат на вызовы этих функций для них.
Например, вы можете перегрузить оператор унарного минуса:
data class Point(val x: Int, val y: Int) operator fun Point.unaryMinus() = Point(-x, -y) fun main() < val point = Point(10, 20) println(-point) // выведет "Point(x=-10, y=-20)" >
Инкремент и декремент
| Выражение | Транслируется в |
|---|---|
| a++ | a.inc() + см. ниже |
| a— | a.dec() + см. ниже |
Функции inc() и dec() должны возвращать значение, которое будет присвоено переменной, к которой была применена операция ++ или — . Они не должны изменять сам объект, для которого были вызваны эти функции.
Компилятор осуществляет следующие шаги, обрабатывая операторы в постфиксной форме, например a++ :
- Определяет тип переменной a , пусть это будет T ;
- Ищет функцию inc() с модификатором operator без параметров, применимую для приёмника типа Т .
- Проверяет, что возвращаемый тип такой функции является подтипом T .
Результатами вычисления выражения является:
- Сохранение инициализирующего значения a во временную переменную a0 ,
- Сохранение результата выполнения a0.inc() в a ,
- Возвращение a0 как результата вычисления выражения (т.е. значения до инкремента).
Для a— шаги выполнения полностью аналогичные.
Для префиксной формы ++a или —a действует также, но результатом будет:
- Присвоение результата вычисления a.inc() непосредственно a ,
- Возвращение нового значения a как результата вычисления выражения.
Бинарные операции
Арифметические операции
| Выражение | Транслируется в |
|---|---|
| a + b | a.plus(b) |
| a — b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.rem(b) |
| a..b | a.rangeTo(b) |
Для перечисленных в таблице операций компилятор всего лишь решает выражение из колонки Транслируется в.
Ниже приведен пример класса Counter (счетчик), начинающего счёт с заданного значения, которое может быть увеличено с помощью перегруженного оператора + :
data class Counter(val dayIndex: Int) < operator fun plus(increment: Int): Counter < return Counter(dayIndex + increment) >>
Оператор in
| Выражение | Транслируется в |
|---|---|
| a in b | b.contains(a) |
| a !in b | !b.contains(a) |
Для in и !in используется одна и та же процедура, только возвращаемый результат инвертируется.
Оператор доступа по индексу
| Выражение | Транслируется в |
|---|---|
| a[i] | a.get(i) |
| a[i, j] | a.get(i, j) |
| a[i_1, . i_n] | a.get(i_1, . i_n) |
| a[i] = b | a.set(i, b) |
| a[i, j] = b | a.set(i, j, b) |
| a[i_1, . i_n] = b | a.set(i_1, . i_n, b) |
Квадратные скобки транслируются в вызов get или set с соответствующим числом аргументов.
Оператор вызова
| Выражение | Транслируется в |
|---|---|
| a() | a.invoke() |
| a(i) | a.invoke(i) |
| a(i, j) | a.invoke(i, j) |
| a(i_1, . i_n) | a.invoke(i_1, . i_n) |
Оператор вызова (функции, метода) в круглых скобках транслируется в invoke с соответствующим числом аргументов.
Присвоения с накоплением
| Выражение | Транслируется в |
|---|---|
| a += b | a.plusAssign(b) |
| a -= b | a.minusAssign(b) |
| a *= b | a.timesAssign(b) |
| a /= b | a.divAssign(b) |
| a %= b | a.modAssign(b) |
Для присваивающих операций, таких как a += b , компилятор осуществляет следующие шаги:
- Если функция из правой колонки таблицы доступна:
- Если соответствующая бинарная функция (например plus() для plusAssign() ) также доступна, a — изменяемая переменная и возвращаемый тип plus является подтипом типа a , то фиксируется ошибка (неоднозначность);
- Проверяется, что возвращаемое значение функции Unit , в противном случае фиксируется ошибка;
- Генерируется код для a.plusAssign(b) .
Присвоение НЕ ЯВЛЯЕТСЯ выражением в Kotlin.
Операторы равенства и неравенства
Выражение Транслируется в a == b a?.equals(b) ?: (b === null) a != b !(a?.equals(b) ?: (b === null)) Эти операторы работают только с функцией equals(other: Any?): Boolean , которая может быть переопределена для обеспечения пользовательской реализации проверки равенства. Любая другая функция с тем же именем (например, equals(other: Foo) ) вызываться не будет.
`===` and `!==` (identity checks) are not overloadable, so no conventions exist for them. —>
Операции === и !== (проверка идентичности) являются неперегружаемыми, поэтому никакие соглашения для них не приводятся.
Операция == имеет специальный смысл: она транслируется в составное выражение, в котором экранируются значения null . null == null — это всегда истина, а x == null для non-null значений x — всегда ложь, и не будет расширяться в x.equals() .
Операторы сравнений
Выражение Транслируется в a > b a.compareTo(b) > 0 a < b a.compareTo(b) < 0 a >= b a.compareTo(b) >= 0 a a.compareTo(b) Все сравнения транслируются в вызовы compareTo , от которых требуется возврат значения типа Int .
Операторы делегирования свойств
Операторы provideDelegate , getValue и setValue описаны в Делегированные свойства.
Инфиксные вызовы именованных функций
Вы можете имитировать инфиксные операции, используя инфиксную запись.
© 2015—2024 Open Source Community
Высокоуровневые функции и лямбды
В Kotlin функции являются функциями первого класса. Это значит, что они могут храниться в переменных и структурах данных, передаваться в качестве аргументов и возвращаться из других функций высшего порядка. Вы можете работать с функциями любым способом, который возможен для других нефункциональных значений.
Чтобы это облегчить, Kotlin, как статически типизированный язык программирования, использует семейство функциональных типов для представления функций и предоставляет набор специализированных языковых конструкций, таких как лямбда-выражения.
Функции высшего порядка
Функция высшего порядка — это функция, которая принимает функции как параметры, или возвращает функцию в качестве результата.
Хорошим примером такой функции является идиома функционального программирования fold для коллекций, которая принимает начальное значение — accumulator вместе с комбинирующей функцией и строит возвращаемое значение, последовательно комбинируя текущее значение accumulator с каждым элементом коллекции, заменяя значение accumulator .
fun Collection.fold( initial: R, combine: (acc: R, nextElement: T) -> R ): R < var accumulator: R = initial for (element: T in this) < accumulator = combine(accumulator, element) >return accumulator >R`, so it accepts a function that takes two arguments of types `R` and `T` and returns a value of type `R`. It is [invoked](#invoking-a-function-type-instance) inside the `for` loop, and the return value is then assigned to `accumulator`. —>
В приведённом выше коде параметр combine имеет функциональный тип (R, T) -> R , поэтому он принимает функцию, которая принимает два аргумента типа R и T и возвращает значение типа R . Он вызывается внутри цикла for и присваивает accumulator возвращаемое значение.
Чтобы вызвать fold , вы должны передать ему экземпляр функционального типа в качестве аргумента и лямбда-выражение (описание ниже). Лямбда-выражения часто используются в качестве параметра функции высшего порядка.
fun main() < val items = listOf(1, 2, 3, 4, 5) // Лямбда - это блок кода, заключенный в фигурные скобки. items.fold(0, < // Если у лямбды есть параметры, то они указываются перед знаком '->' acc: Int, i: Int -> print("acc = $acc, i = $i, ") val result = acc + i println("result = $result") // Последнее выражение в лямбде считается возвращаемым значением: result >) // Типы параметров в лямбде необязательны, если они могут быть выведены: val joinedToString = items.fold("Elements:", < acc, i ->acc + " " + i >) // Ссылки на функции также могут использоваться для вызовов функций высшего порядка: val product = items.fold(1, Int::times) println("joinedToString = $joinedToString") println("product = $product") >Функциональные типы
String`, for declarations that deal with functions: `val onClick: () -> Unit = . `. —>
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 .
Вы также можете присвоить функциональному типу альтернативное имя, используя псевдонимы типов.
typealias ClickHandler = (Button, ClickEvent) -> UnitСоздание функционального типа
Существует несколько способов получить экземпляр функционального типа:
- Используя блок с кодом внутри функционального литерала в одной из форм:
- лямбда-выражение: < a, b ->a + b > ,
- анонимная функция: fun(s: String): Int
Литералы функций с объектом-приёмником могут использоваться как значения функциональных типов с получателем.
- Используя вызываемую ссылку на существующее объявление:
- функции верхнего уровня, локальной функции, функции-члена или функции-расширения: ::isOdd , String::toInt ,
- свойства верхнего уровня, члена или свойства-расширения: List::size ,
- конструктора: ::Regex
К ним относятся привязанные вызываемые ссылки, которые указывают на член конкретного экземпляра: foo::toString .
- Используя экземпляр пользовательского класса, который реализует функциональный тип в качестве интерфейса:
class IntTransformer: (Int) -> Int < override operator fun invoke(x: Int): Int = TODO() >val intFunction: (Int) -> Int = IntTransformer()При достаточной информации компилятор может самостоятельно вывести функциональный тип для переменной.
val a = < i: Int ->i + 1 > // Выведенный тип - (Int) -> IntC` 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 , и наоборот.
fun main() < val repeatFun: String.(Int) ->String = < times ->this.repeat(times) > val twoParameters: (String, Int) -> String = repeatFun // OK fun runTransformation(f: (String, Int) -> String): String < return f("hello", 3) >val result = runTransformation(repeatFun) // OK println("result = $result") >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) .
fun main() < val stringPlus: (String, String) ->String = String::plus val intPlus: Int.(Int) -> Int = Int::plus println(stringPlus.invoke("")) println(stringPlus("Hello, ", "world!")) println(intPlus.invoke(1, 1)) println(intPlus(1, 2)) println(2.intPlus(3)) // вызывается как функция-расширение >Встроенные функции
Иногда выгодно улучшить производительность функций высшего порядка, используя встроенные функции (ориг.: inline functions).
Лямбда-выражения и анонимные функции
Лямбда-выражения и анонимные функции — это «функциональный литерал», то есть необъявленная функция, которая немедленно используется в качестве выражения. Рассмотрим следующий пример:
max(strings, < a, b ->a.length < b.length >)Функция max является функцией высшего порядка, потому что она принимает функцию в качестве второго аргумента. Этот второй аргумент является выражением, которое в свою очередь есть функция, то есть функциональный литерал. Как функция он эквивалентен объявлению:
fun compare(a: String, b: String): Boolean = a.length < b.lengthСинтаксис лямбда-выражений
Полная синтаксическая форма лямбда-выражений может быть представлена следующим образом:
val sum: (Int, Int) -> Int = < x: Int, y: Int ->x + y >- Лямбда-выражение всегда заключено в скобки ;
- Объявление параметров при таком синтаксисе происходит внутри этих скобок и может включать в себя аннотации типов;
- Тело функции начинается после знака -> ;
- Если тип возвращаемого значения не Unit , то в качестве возвращаемого типа принимается последнее (а возможно и единственное) выражение внутри тела лямбды.
Если вы вынесите все необязательные объявления, то, что останется, будет выглядеть следующим образом:
val sum = < x: Int, y: Int ->x + y >Передача лямбды в качестве последнего параметра
В Kotlin существует соглашение: если последний параметр функции является функцией, то лямбда-выражение, переданное в качестве соответствующего аргумента, может быть вынесено за круглые скобки.
val product = items.fold(1) < acc, e ->acc * e >Такой синтаксис также известен как trailing lambda.
Когда лямбда-выражение является единственным аргументом функции, круглые скобки могут быть опущены.
it: неявное имя единственного параметра
Очень часто лямбда-выражение имеет только один параметр.
` can be omitted. The parameter will be implicitly declared under the name `it`: -->
Если компилятор способен самостоятельно определить сигнатуру, то объявление параметра можно опустить вместе с -> . Параметр будет неявно объявлен под именем it .
ints.filter < it >0 > // этот литерал имеет тип '(it: Int) -> Boolean'Возвращение значения из лямбда-выражения
Вы можете вернуть значение из лямбды явно, используя оператор return. Либо неявно будет возвращено значение последнего выражения.
Таким образом, два следующих фрагмента равнозначны:
ints.filter < val shouldFilter = it >0 shouldFilter > ints.filter < val shouldFilter = it >0 return@filter shouldFilter >Это соглашение, вместе с передачей лямбда-выражения вне скобок, позволяет писать код в стиле LINQ.
strings.filter < it.length == 5 >.sortedBy < it >.map
Символ подчеркивания для неиспользуемых переменных
Если параметр лямбды не используется, то разрешено его имя заменить на символ подчёркивания.
map.forEach < _, value ->println("$value!") >Деструктуризация в лямбдах
Деструктуризация в лямбдах описана в Деструктурирующие объявления.
Анонимные функции
Единственной особенностью синтаксиса лямбда-выражений, о которой ещё не было сказано, является способность определять и назначать возвращаемый функцией тип. В большинстве случаев в этом нет особой необходимости, потому что он может быть вычислен автоматически. Однако, если у вас есть потребность в определении возвращаемого типа, вы можете воспользоваться альтернативным синтаксисом: анонимной функцией.
fun(x: Int, y: Int): Int = x + yОбъявление анонимной функции выглядит очень похоже на обычное объявление функции, за исключением того, что её имя опущено. Тело такой функции может быть описано и выражением (как показано выше), и блоком.
fun(x: Int, y: Int): Int
Параметры функции и возвращаемый тип обозначаются таким же образом, как в обычных функциях, за исключением того, что тип параметра может быть опущен, если его значение следует из контекста.
ints.filter(fun(item) = item > 0)Аналогично и с типом возвращаемого значения: он вычисляется автоматически для функций-выражений или же должен быть явно определён (если не является типом 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 , в свою очередь, выйдет, собственно, из анонимной функции.
Замыкания
Лямбда-выражение или анонимная функция (так же, как и локальная функция или анонимные объекты) имеет доступ к своему замыканию, то есть к переменным, объявленным вне этого выражения или функции. Переменные, захваченные в замыкании, могут быть изменены в лямбде.
var sum = 0 ints.filter < it >0 >.forEach < sum += it >print(sum)Литералы функций с объектом-приёмником
C`, can be instantiated with a special form of function literals – function literals with receiver. -->
Функциональные типы с получателем, такие как A.(B) -> C , могут быть вызваны с помощью особой формы – литералов функций с объектом-приёмником.
Как было сказано выше, Kotlin позволяет вызывать экземпляр функционального типа с получателем, предоставляющим объект-приёмник.
Внутри тела литерала объект-приёмник, переданный при вызове функции, становится неявным this , поэтому вы можете получить доступ к членам этого объекта-приёмника без каких-либо дополнительных определителей, а обращение к самому объекту-приёмнику осуществляется с помощью выражения this .
Это схоже с принципом работы функций-расширений, которые позволяют получить доступ к членам объекта-приёмника внутри тела функции.
Ниже приведён пример литерала с получателем вместе с его типом, где plus вызывается для объекта-приёмника:
val sum: Int.(Int) -> Int = < other ->plus(other) >Синтаксис анонимной функции позволяет вам явно указать тип приёмника. Это может быть полезно в случае, если вам нужно объявить переменную типа нашей функции для использования в дальнейшем.
val sum = fun Int.(other: Int): Int = this + otherЛямбда-выражения могут быть использованы как литералы функций с приёмником, когда тип приёмника может быть выведен из контекста. Один из самых важных примеров их использования это типобезопасные строители (ориг.: type-safe builders).
class HTML < fun body() < . >> fun html(init: HTML.() -> Unit): HTML < val html = HTML() // создание объекта-приёмника html.init() // передача приёмника в лямбду return html >html < // лямбда с приёмником начинается тут body() // вызов метода объекта-приёмника >Invoke kotlin зачем нужен

Комментарии
Популярные По порядку
Не удалось загрузить комментарии.ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ
10 полезных книг для Java программиста на русском языке
Небольшая подборка интересных и полезных книг для Java программиста по самому языку, фреймворкам и программированию в целом.
Зачем учить язык Kotlin?
Kotlin на текущий момент однозначно находится на пике популярности и может принести немало пользы в тестировании. Но не все так просто, в чем вы можете убедиться из этой статьи.
20 отличных ресурсов для изучения Kotlin
Данная подборка материалов связана с изучением нового, и теперь уже официального языка Android-разработки Kotlin. Изучив данную подборку, возможно, вам захочется узнать о Kotlin больше!