HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-03-10
1 <p>Kotlin - простой и совместимый с Java язык программирования, который сокращает время написания кода за счет более коротких конструкций. В этой статье мы расскажем про несколько популярных способов "магического" применения функций в Kotlin.</p>
1 <p>Kotlin - простой и совместимый с Java язык программирования, который сокращает время написания кода за счет более коротких конструкций. В этой статье мы расскажем про несколько популярных способов "магического" применения функций в Kotlin.</p>
2 <h2>Extension Functions</h2>
2 <h2>Extension Functions</h2>
3 <p>Первый способ - расширение классов без наследования. Он позволяет, не меняя класс String и использующие его пакеты, расширить данный класс путём добавления нового метода или свойства.</p>
3 <p>Первый способ - расширение классов без наследования. Он позволяет, не меняя класс String и использующие его пакеты, расширить данный класс путём добавления нового метода или свойства.</p>
4 <p>Представьте, что у нас есть метод deleteSpaces():</p>
4 <p>Представьте, что у нас есть метод deleteSpaces():</p>
5 private fun String.deleteSpaces(): String { return this.replace(" ", "") }<p>Мы можем использовать этот метод таким образом, как будто он - это часть класса String. Пользователь увидит это вот так:</p>
5 private fun String.deleteSpaces(): String { return this.replace(" ", "") }<p>Мы можем использовать этот метод таким образом, как будто он - это часть класса String. Пользователь увидит это вот так:</p>
6 println("Hel lo, Wor ld".deleteSpaces()) // Hello,World<p>После выполнения компиляции этот метод уже будет выглядеть приблизительно так (код упрощён, главное - понять суть):</p>
6 println("Hel lo, Wor ld".deleteSpaces()) // Hello,World<p>После выполнения компиляции этот метод уже будет выглядеть приблизительно так (код упрощён, главное - понять суть):</p>
7 private static final String deleteSpaces(@NotNull String receiver) { return receiver.replace(" ", ""); }<p>Следовательно, делаем вывод, что внутри метода deleteSpaces() у нас присутствует доступ лишь к публичным полям и методам класса, в результате чего инкапсуляция не нарушается.</p>
7 private static final String deleteSpaces(@NotNull String receiver) { return receiver.replace(" ", ""); }<p>Следовательно, делаем вывод, что внутри метода deleteSpaces() у нас присутствует доступ лишь к публичным полям и методам класса, в результате чего инкапсуляция не нарушается.</p>
8 <p>Кроме Extension Functions, в Котлин по аналогии могут быть и Extension Properties:</p>
8 <p>Кроме Extension Functions, в Котлин по аналогии могут быть и Extension Properties:</p>
9 private val String.first: Char get() { return this[0] } print("Kotlin".first) // K<p>Большая часть "магических" применений лямбд и функций в Котлин, по сути, являются не более, чем синтаксическим сахаром, но зато каким удобным!</p>
9 private val String.first: Char get() { return this[0] } print("Kotlin".first) // K<p>Большая часть "магических" применений лямбд и функций в Котлин, по сути, являются не более, чем синтаксическим сахаром, но зато каким удобным!</p>
10 <h2>Анонимные функции и лямбда-функции</h2>
10 <h2>Анонимные функции и лямбда-функции</h2>
11 <p>Анонимные и лямбда-функции в Kotlin - это функции без имени, однако при этом их можно использовать в качестве объектов. И писать их можно непосредственно внутри выражения без отдельного объявления.</p>
11 <p>Анонимные и лямбда-функции в Kotlin - это функции без имени, однако при этом их можно использовать в качестве объектов. И писать их можно непосредственно внутри выражения без отдельного объявления.</p>
12 <p>Рассмотрим синтаксис лямбда-выражения:</p>
12 <p>Рассмотрим синтаксис лямбда-выражения:</p>
13 { аргументы -&gt; возвращаемый_тип тело_функции }<p>Что касается анонимной функции, то синтаксис её декларации абсолютно совпадает с синтаксисом обычной функции, просто у первой нет имени.</p>
13 { аргументы -&gt; возвращаемый_тип тело_функции }<p>Что касается анонимной функции, то синтаксис её декларации абсолютно совпадает с синтаксисом обычной функции, просто у первой нет имени.</p>
14 fun defaultFun(x: Int, y: Int): Int = x + y // Именованная функция val f = fun(x: Int, y: Int): Int = x + y // Анонимная функция<p>Теперь давайте посмотрим на использование лямбд совместно с функциями высшего порядка.</p>
14 fun defaultFun(x: Int, y: Int): Int = x + y // Именованная функция val f = fun(x: Int, y: Int): Int = x + y // Анонимная функция<p>Теперь давайте посмотрим на использование лямбд совместно с функциями высшего порядка.</p>
15 <h2>Функции высшего порядка</h2>
15 <h2>Функции высшего порядка</h2>
16 <p>Речь идёт о функции, принимающей в виде одного из аргументов другую функцию, включая лямбду либо анонимную функцию. Ярчайший пример применения -<strong>callback</strong>.</p>
16 <p>Речь идёт о функции, принимающей в виде одного из аргументов другую функцию, включая лямбду либо анонимную функцию. Ярчайший пример применения -<strong>callback</strong>.</p>
17 <p>Представьте, что у нас есть функция высшего порядка longWork():</p>
17 <p>Представьте, что у нас есть функция высшего порядка longWork():</p>
18 private fun longWork(callback: (arg1: Int, arg2: String) -&gt; Unit) { val result = doSomething() callback(result, "Kotlin &gt; Java") // вызов callback }<p>Эта функция принимает в виде аргумента функцию callback(), однако вызывает её лишь после функции doSomething():</p>
18 private fun longWork(callback: (arg1: Int, arg2: String) -&gt; Unit) { val result = doSomething() callback(result, "Kotlin &gt; Java") // вызов callback }<p>Эта функция принимает в виде аргумента функцию callback(), однако вызывает её лишь после функции doSomething():</p>
19 longWork({ arg1, arg2 -&gt; Unit print("callback runned") })<p>Как видите, мы вызываем функцию longWork(), передавая ей в виде аргумента лямбда-функцию, которую она вызовет позже. Язык программирования Котлин даёт возможность выносить лямбду за скобки в том случае, если она является последним аргументом функции, а также вообще убирать скобки, если лямбда - единственный аргумент. Кроме этого, чаще всего мы можем убрать тип возвращаемого значения, заменив аргументы на _ в том случае, если они не используются. Вот как выглядит укороченный вариант кода:</p>
19 longWork({ arg1, arg2 -&gt; Unit print("callback runned") })<p>Как видите, мы вызываем функцию longWork(), передавая ей в виде аргумента лямбда-функцию, которую она вызовет позже. Язык программирования Котлин даёт возможность выносить лямбду за скобки в том случае, если она является последним аргументом функции, а также вообще убирать скобки, если лямбда - единственный аргумент. Кроме этого, чаще всего мы можем убрать тип возвращаемого значения, заменив аргументы на _ в том случае, если они не используются. Вот как выглядит укороченный вариант кода:</p>
20 longWork { _, _ -&gt; print("callback runned") }<p>Всё это внешне уже напоминает не функцию высшего порядка, а языковую конструкцию, как, к примеру synchronized в Java. Кстати, synchronized в Котлин построен именно как функция высшего порядка.</p>
20 longWork { _, _ -&gt; print("callback runned") }<p>Всё это внешне уже напоминает не функцию высшего порядка, а языковую конструкцию, как, к примеру synchronized в Java. Кстати, synchronized в Котлин построен именно как функция высшего порядка.</p>
21 <p>Всё это весьма удобно при создании DSL - предметно-ориентированных языков. Допустим, того же Anko (Android UI прямо в Kotlin с сохранением удобства XML-разметки), или kotlinx.html (по аналогии с Anko), или Gradle Kotlin DSL (Gradle-скрипты на Котлин).</p>
21 <p>Всё это весьма удобно при создании DSL - предметно-ориентированных языков. Допустим, того же Anko (Android UI прямо в Kotlin с сохранением удобства XML-разметки), или kotlinx.html (по аналогии с Anko), или Gradle Kotlin DSL (Gradle-скрипты на Котлин).</p>
22 <p>Давайте посмотрим на HTML-страницу на Kotlin:</p>
22 <p>Давайте посмотрим на HTML-страницу на Kotlin:</p>
23 System.out.appendHTML().html { body { div { a("http://kotlinlang.org") { target = ATarget.blank +"Main site" } } } }<p>Вывод в stdout будет следующим:</p>
23 System.out.appendHTML().html { body { div { a("http://kotlinlang.org") { target = ATarget.blank +"Main site" } } } }<p>Вывод в stdout будет следующим:</p>
24 &lt;html&gt; &lt;body&gt; &lt;div&gt;&lt;a href="http://kotlinlang.org" target="_blank"&gt;Main site&lt;/a&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt;<p>Главный плюс этого DSL заключается в том, что в Kotlin существуют переменные, которые можно использовать для генерации динамической страницы (в отличие, скажем, от декларативного HTML). И всё это красивее и удобнее классической генерации страниц посредством конкатенации множества строк. Но вообще на практике для генерации HTML-разметки применяют иные методы, а этот мы показали лишь в качестве примера DSL в Kotlin.</p>
24 &lt;html&gt; &lt;body&gt; &lt;div&gt;&lt;a href="http://kotlinlang.org" target="_blank"&gt;Main site&lt;/a&gt;&lt;/div&gt; &lt;/body&gt; &lt;/html&gt;<p>Главный плюс этого DSL заключается в том, что в Kotlin существуют переменные, которые можно использовать для генерации динамической страницы (в отличие, скажем, от декларативного HTML). И всё это красивее и удобнее классической генерации страниц посредством конкатенации множества строк. Но вообще на практике для генерации HTML-разметки применяют иные методы, а этот мы показали лишь в качестве примера DSL в Kotlin.</p>
25 <p>Кроме того, возможно использование функций высшего порядка в качестве аналога Streams API из Java:</p>
25 <p>Кроме того, возможно использование функций высшего порядка в качестве аналога Streams API из Java:</p>
26 listOf(1, 2, 3, 4, 5) .filter { n -&gt; n % 2 == 1 } // 1, 3, 5 .map { n -&gt; n * n } // 1, 9, 25 .drop(1) // 9, 25 .take(1) // 9<h2>Ещё несколько слов о Kotlin</h2>
26 listOf(1, 2, 3, 4, 5) .filter { n -&gt; n % 2 == 1 } // 1, 3, 5 .map { n -&gt; n * n } // 1, 9, 25 .drop(1) // 9, 25 .take(1) // 9<h2>Ещё несколько слов о Kotlin</h2>
27 <p>В Kotlin, в отличие от Джавы, присутствуют перегружаемые операторы. К примеру, если у класса есть метод plus(), мы можем вызвать его оператором +, а метод get() - с помощью оператора []. Это во-первых.</p>
27 <p>В Kotlin, в отличие от Джавы, присутствуют перегружаемые операторы. К примеру, если у класса есть метод plus(), мы можем вызвать его оператором +, а метод get() - с помощью оператора []. Это во-первых.</p>
28 <p>Во-вторых, функции в Котлин можно пометить явно как inline либо noinline. Такой модификатор говорит компилятору, стоит ли заинлайнить функцию в целях повышения производительности либо нет. Но тут есть нюанс, связанный с разным поведением return в inline- и noinline-функциях.</p>
28 <p>Во-вторых, функции в Котлин можно пометить явно как inline либо noinline. Такой модификатор говорит компилятору, стоит ли заинлайнить функцию в целях повышения производительности либо нет. Но тут есть нюанс, связанный с разным поведением return в inline- и noinline-функциях.</p>
29 <p>В inline-функциях return будет произведен из ближайшей по области видимости noinline-функции. А в noinline - непосредственно из самой функции. Данную проблему решают "именованные return".</p>
29 <p>В inline-функциях return будет произведен из ближайшей по области видимости noinline-функции. А в noinline - непосредственно из самой функции. Данную проблему решают "именованные return".</p>
30 <p>Чтобы сделать return из лямбды, которую мы передаём в вышеописанном примере в apply(), мы можем задействовать return@apply.</p>
30 <p>Чтобы сделать return из лямбды, которую мы передаём в вышеописанном примере в apply(), мы можем задействовать return@apply.</p>
31 <p>Кстати, именованными могут быть не только return, но и break, continue. Кроме того, есть возможность создавать и свои метки:</p>
31 <p>Кстати, именованными могут быть не только return, но и break, continue. Кроме того, есть возможность создавать и свои метки:</p>
32 loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }<p>Также есть модификатор функции tailrec, который сообщает компилятору заменить рекурсию в функции на цикл, если функция написана в функциональном формате return if-then-else:</p>
32 loop@ for (i in 1..100) { for (j in 1..100) { if (...) break@loop } }<p>Также есть модификатор функции tailrec, который сообщает компилятору заменить рекурсию в функции на цикл, если функция написана в функциональном формате return if-then-else:</p>
33 tailrec fun findFixPoint(x: Double = 1.0): Double = when { x == cos(x) -&gt; x else -&gt; findFixPoint(cos(x)) } // При компиляции будет заменена на fun findFixPoint(): Double { var x = 1.0 while (true) { val y = cos(x) if (x == y) return x x = y } }<p>В-третьих, в ситуации, когда метод требует в виде аргументов объект, который должен быть унаследован от класса/интерфейса с одним абстрактным методом, в эту функцию мы можем передать лямбду либо анонимную функцию, а компилятор самостоятельно создаст анонимный класс с переопределением абстрактного метода на нашу лямбду. К примеру, в стандартной Android-библиотеке существует метод public void setOnClickListener(View.OnClickListener l), в котором OnClickListener представляет собой интерфейс с единственным методом onClick(View v).</p>
33 tailrec fun findFixPoint(x: Double = 1.0): Double = when { x == cos(x) -&gt; x else -&gt; findFixPoint(cos(x)) } // При компиляции будет заменена на fun findFixPoint(): Double { var x = 1.0 while (true) { val y = cos(x) if (x == y) return x x = y } }<p>В-третьих, в ситуации, когда метод требует в виде аргументов объект, который должен быть унаследован от класса/интерфейса с одним абстрактным методом, в эту функцию мы можем передать лямбду либо анонимную функцию, а компилятор самостоятельно создаст анонимный класс с переопределением абстрактного метода на нашу лямбду. К примеру, в стандартной Android-библиотеке существует метод public void setOnClickListener(View.OnClickListener l), в котором OnClickListener представляет собой интерфейс с единственным методом onClick(View v).</p>
34 <p>Кстати, лямбда, переданная в качестве setOnClickListener { doSomething() }, скомпилируется в анонимный класс, реализующий интерфейс OnClickListener, в котором лямбда превратится в метод onClick(View v).</p>
34 <p>Кстати, лямбда, переданная в качестве setOnClickListener { doSomething() }, скомпилируется в анонимный класс, реализующий интерфейс OnClickListener, в котором лямбда превратится в метод onClick(View v).</p>
35 <h2>Выводы</h2>
35 <h2>Выводы</h2>
36 <p>Язык программирования Kotlin с помощью своих "магических" функций существенно упрощает написание и, что не менее важно, чтение кода. А удобство и безопасность - наиболее важные отличия Kotlin от созданной в далёком 1995 году Java, когда о безопасности и удобстве мы только мечтали.</p>
36 <p>Язык программирования Kotlin с помощью своих "магических" функций существенно упрощает написание и, что не менее важно, чтение кода. А удобство и безопасность - наиболее важные отличия Kotlin от созданной в далёком 1995 году Java, когда о безопасности и удобстве мы только мечтали.</p>
37 <p><a>Источник</a></p>
37 <p><a>Источник</a></p>
38  
38