Let kotlin что это
Перейти к содержимому

Let kotlin что это

  • автор:

with, apply, also, run, let

Функция with позволяет выполнить несколько операций над одним объектом, не повторяя его имени.

Функция принимает два аргумента — объект и лямбда-выражение. Первый аргумент преобразуется в получатель лямбда-выражения. К получателю можно обращаться через this.

 class Cat < var name:String? = null var breed:String? = null >val murzik = Cat() murzik.name = "Murzik" // Вместо val murzikName = murzik.name val murzikBreed = murzik.breed println("$murzikName:$murzikBreed") // Укороченный вариант with(murzik)

Нам уже не нужно каждый раз упоминать имя объекта.

Ещё один пример посложнее.

 // выводим все буквы алфавита fun printAlphabet() = with(StringBuilder()) < for (letter in 'A'..'Z')< append(letter) >toString() > println(printAlphabet()) // ABCDEFGHIJKLMNOPQRSTUVWXYZ 

Функция возвращает результат последнего выражения в теле лямбда-выражения. Если вам нужен объект-получатель, то используйте apply.

apply

Функция apply работает почти так же, как with, но возвращает объект, переданный в аргументе.

 fun printAlphabet() = StringBuilder().apply < for (letter in 'A'..'Z')< append(letter) >>.toString() println(printAlphabet())// ABCDEFGHIJKLMNOPQRSTUVWXYZ 

Полезна в тех случаях, когда требуется создание экземпляра, у которого следует инициализировать некоторые свойства. Часто в этих случаях мы просто повторяем имя экземпляра.

 val button = findViewById(R.id.button) button.text = "I am a button" button.textSize = 18.0F button.setBackgroundColor(Color.RED) 

Инициализируем настройки кнопки через apply.

 val button = findViewById(R.id.button) button.apply

Применим к коту, поменяв возраст.

 class Cat(var age: Int) val result = Cat(5).apply < age = 8 >println(result.age) // 8 

also

Метод для обмена значениями между двумя переменными без участия третьей переменной.

 var x = 100 var y = 25 x = y.also < y = x >println(x) println(y) class Cat(var age: Int) val result = Cat(5).also < it.age = 8 >println(result.age) // 8 
 val game: String = StringBuilder().also < it.append("tic") it.append("tac") it.append("toe") >.toString() println(game) 

let

let полезен при работе с объектами, которые могут принимать значение null. Вместо того, чтобы создавать длинные цепочки выражений if-else, можно просто скомбинировать оператор ? («оператор безопасного вызова») с let: в результате мы получим лямбду, у которой аргумент it является не nullable-версией исходного объекта.

 var cat: String? = null cat?.let // не выводится cat = "Barsik" cat?.let

Второй вариант использования — преобразовать один тип в другой.

run

Узнаем настроение кота.

 fun testCat() < var mood = "I am sad" run < val mood = "I am happy" println(mood) // I am happy >println(mood) // I am sad > Logcat: I/System.out: I am happy I/System.out: I am sad 

В функции дважды используется переменная с одним именем, но они не мешают друг другу и выводят разные сообщения, не переопределяя своих значений.

Это был простейший и достаточно бесполезный пример. В реальности он используется немного иначе.

Защита от null, ключевое слово let

В Java существует проблема с исключением NullPointerException и разработчику нужно постоянно устраивать проверки на null. В Kotlin существует защита от подобных ошибок. Нужно использовать вопросительный знак ? у типа.

 // Не скомпилируется. Кот не может быть null var notNullCat: Cat = null // Кот может быть null var cat: Cat? = null // Не скомпилируется, кот может быть null и мы должны учитывать это cat.print() // Можем печатать, если cat != null cat?.print() // Умное приведение. Если мы проводили проверку if, // то можно вызывать обычным способом // checked nullity if (cat != null) < cat.print() >// Если уверены, что объект не null. Иначе выбросит исключение cat. print() // Используйте элвис-оператор, чтобы дать альтернативное значение, если объект равен null val name = cat?.name ?: "empty" 

Любой объект может быть null и мы можем явно указать это через символ вопроса ?. В этом случае Kotlin будет проверять на возможность ошибки и предупреждать на этапе разработки.

Любой тип, не поддерживающий null, является дочерним типом соответствующего типа, поддерживающего null, поэтому не путайте следующие выражения:

 // Так можно val x: Int = 9 val y: Int? = x // Так нельзя val x: Int? = 9 val y: Int = x 

Умное приведение Smart cast позволяет превратить переменную из одного состояния в другое: cat? превратится в cat (см. пример выше) или переменная a типа Int? превратится в Int.

 val a: Int? = null if (a != null) < a.toLong() // теперь это Int >

Если умное приведение вам не нужно, то используйте запись с безопасным оператором ?.

 val a: Int? = null a?.toLong() 

Функция будет вызвана только в том случае, если значение a отлично от null. Безопасные вызовы можно сцеплять.

Не путайте null с пустой строкой «». Выводим значение переменной типа String.

 private var catName: String? = null println(catName) // Выводится следующее null 

Любую функцию или параметр конструктора можно объявить с null-совместимым типом. Следующий код определяет функцию printInt(), которая получает параметр типа Int? (null-совместимый Int):

 fun printInt(x: Int?)

Функция может иметь null-совместимый возвращаемый тип.

 fun result(): Long?

Можно создать массив нулевых объектов. При этом массив может содержать и строки и null.

 var cats: Array = arrayOf("Васька", "Мурзик", null) 

Элвис-оператор ?:

Другой «Элвис-оператор» ?: (напоминает причёску Элвиса Пресли, если повернуть голову, как на смайликах) позволяет назначить альтернативное значение, если объект равен null.

 val a: Int? = null val myLong = a?.toLong() ?: 0L var cat: Cat? = null // Если null, то вернуть "Без имени" val string: String = cat?.name ?: "Без имени" println(string) 

Справа от элвис-оператора можно использовать return и выбрасывать исключения.

 val myLong = a?.toLong() ?: return false val myLong = a?.toLong() ?: throw IllegalStateException() 

Оператор !!

Если вы точно уверены, что ваша переменная не null, то можете использовать оператор !!. Kotlin будет полагаться на ваш профессионализм и не станет проверять ваше предположение. Если вы ошиблись в своём предположении, то ваше приложение может грохнуться.

 // скомпилируется, но приложение грохнется val a: Int? = null a. toLong() 

Например, в Android часто объявляются компоненты, а инициализация происходит позже.

 // объявляем var button: Button? = null override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // инициализируем button = findViewById(R.id.button) button?.setOnClickListener(View.OnClickListener < >) > 

Подобный способ позволяет избежать null и аналогичен записи в Java.

 if (button != null) < button.setOnClickListener(/* Ваш код */); >

При полной уверенности можете написать

 button. setOnClickListener < /* */ >

Ключевое слово let

Ещё один способ избежать проблем с null — это использовать ключевое слово let вместе с оператором ?.:

 cat?.let

Код будет выводить имя кота только в том случае, если объект не равен null.

Этот подход удобен, когда имеются смешанные варианты и мы хотим выполнения кода только для объектов, которые не имеют значения null:

 var array = arrayOf("Мурзик", "Васька", null) for (item in array) < item?.let< println(it) >> 

В массиве три элемента, но на экран выводятся только два элемента.

Такой подход сокращает и упрощает код. Допустим, у нас есть функция getBestCat(), возвращающий тип Cat?.

 fun getBestCat(): Cat?

Можем вызвать с проверкой на null.

 var cat = getBestCat() if(cat != null)

А можно обойтись без создания новой переменной, а сразу использовать let:

 getBestCat()?.let

Запись означает следующее: получить объект «Самый лучший кот», и если объект не null, то позволить ему есть (eat()).

Let kotlin что это

Scope-функции (можно перевести как «функции контекста» или «функции области видимости») позволяют выполнить для некоторого объекта некоторый код в виде лямбда-выражение. При вызове подобной функции, создается временная область видимости. В этой области видимости можно обращаться к объекту без использования его имени.

В Kotlin есть пять подобных функций: let , run , with , apply и also . Эти функции похожи по своему действию и различаются прежде всего по параметрам и возвращаемым результатам

let

Лямбда-выражение в функции let в качестве параметра it получает объект, для которого вызывается функция. Возвращаемый результат функции let представляет результат данного лямбда-выражения.

inline fun T.let(block: (T) -> R): R

Распространенным сценарием, где применяется данная функция, представляет проверка на null:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let< println("Email: $it") >// Email: sam@gmail.com // аналог без функции let //if(sam.email!=null) println("Email:$") val tom = Person("Tom", null) tom.email?.let < println("Email: $it") >// функция let не будет выполняться > data class Person(val name: String, val email: String?)

Допустим, мы хотим вывести значение свойства Email объекта Person. Но это свойство может хранить значение null (например, если электронный адрес у пользователя не установлен). С помощью выражения sam.email?. проверяем значение свойства email на null . Если email не равно null, то для строки в свойстве email вызывается функция let , которая создает временную область видимости и передает в нее значение свойства email через параметр it . Если же свойство email равно null, то функция let не вызывается.

Если лямбда-выражение вызывает лишь одну функцию, в которую передается параметр it, то можно сократить вызов — указать после оператора :: название вызываемой функции:

fun main() < val sam = Person("Sam", "sam@gmail.com") sam.email?.let(::println) // sam@gmail.com val tom = Person("Tom", "tom@gmail.com") tom.email?.let(::printEmail) // Email: tom@gmail.com >fun printEmail(email: String) < println("Email: $email") >data class Person(val name: String, var email: String?)

with

Лямбда-выражение в функции with в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции with представляет результат данного лямбда-выражения.

inline fun with(receiver: T, block: T.() -> R): R

Функция with принимает объект, для которого надо выполнить блок кода, в качестве параметра. Далее в самом блоке кода мы можем обращаться к общедоступным свойствам и методам объекта без его имени.

Обычно функция with() применяется, когда надо выполнить над объектом набор операций как единое целое:

fun main() < val tom = Person("Tom", null) val emailOfTom = with(tom) < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае функция with получает объект tom, поверяет его свойство email — если оно равно null, то устанавливает его на основе его имени. В качестве результата функции возвращается значение свойства email.

run

Лямбда-выражение в функции run в качестве параметра this получает объект, для которого вызывается функция. Возвращаемый результат функции run представляет результат данного лямбда-выражения.

inline fun T.run(block: T.() -> R): R

Функция run похожа на функцию with за тем исключением, что run реализована как функция расширения. Функция run также принимает объект, для которого надо выполнить блок кода, в качестве параметра. Далее в самом блоке кода мы можем обращаться к общедоступным свойствам и методам объекта без его имени.

fun main() < val tom = Person("Tom", null) val emailOfTom = tom.run < if(email==null) email = "$@gmail.com" email > println(emailOfTom) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае функция run выполняет действия, аналогичные примеру с функцией with .

Реализация run как функции расширения упрощает проверку на null:

fun main() < val tom = Person("Tom", null) val validationResult = tom.email?.run ?: "undefined" println(validationResult) // undefined > data class Person(val name: String, var email: String?)

Также есть другая разновидность функции run() , которая просто позволяет выполнить некоторое лямбда-выражение:

fun main() < val randomText = run< "hello world">println(randomText) // hello world run < println("run function")>// run function >

apply

Лямбда-выражение в функции apply в качестве параметра this получает объект, для которого вызывается функция. Возвращаемым результатом функции фактически является передаваемый в функцию объект для которого выполняется функция.

inline fun T.apply(block: T.() -> Unit): T
fun main() < val tom = Person("Tom", null) tom.apply < if(email==null) email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

В данном случае, как и в примерах с функциями with и run , проверяем значение свойства email, и если оно равно null, устанавливаем его, используя свойство name.

Распространенным сценарием применения функции apply() является построение объекта в виде реализации вариации паттерна «Строитель»:

fun main() < val bob = Employee() bob.name("Bob") bob.age(26) bob.company("JetBrains") println("$($) - $") // Bob (26) - JetBrains > data class Employee(var name: String = "", var age: Int = 0, var company: String = "") < fun age(_age: Int): Employee = apply < age = _age >fun name(_name: String): Employee = apply < name = _name >fun company(_company: String): Employee = apply < company = _company >>

В данном случае класс Employee содержит три метода, которые устанавливают каждое из свойств класса. Причем каждый подобный метод вызывает функцию apply() , которое передает значение соответствующему свойству и возвращает текущий объект Employee.

also

Лямбда-выражение в функции also в качестве параметра it получает объект, для которого вызывается функция. Возвращаемым результатом функции фактически является передаваемый в функцию объект для которого выполняется функция.

inline fun T.also(block: (T) -> Unit): T

Эта функция аналогична функции apply за тем исключением, что внутри also объект, над которым выполняется блок кода, доступен через параметр it :

fun main() < val tom = Person("Tom", null) tom.also < if(it.email==null) it.email = "$@gmail.com" > println(tom.email) // tom@gmail.com > data class Person(val name: String, var email: String?)

Функции области видимости

Стандартная библиотека Kotlin содержит несколько функций, единственной целью которых является выполнение блока кода в контексте объекта. Эти функции формируют временную область видимости для объекта, к которому были применены, и вызывают код, указанный в переданном лямбда-выражении. В этой области видимости можно получить доступ к объекту без явного к нему обращения по имени. Такие функции называются функциями области видимости (англ. scope functions). Всего их пять: let , run , with , apply , и also .

По сути, все эти функции делают одно и то же: выполняют блок кода для объекта. Отличие состоит в том, как этот объект становится доступным внутри блока и каков результат всего выражения.

Пример обычного использования функции области видимости:

data class Person(var name: String, var age: Int, var city: String) < fun moveTo(newCity: String) < city = newCity >fun incrementAge() < age++ >> fun main() < Person("Alice", 20, "Amsterdam").let < println(it) it.moveTo("London") it.incrementAge() println(it) >> 

Если вы захотите написать то же самое без функции let , то вам придется объявить новую переменную и обращаться к ней всякий раз, когда она используется.

data class Person(var name: String, var age: Int, var city: String) < fun moveTo(newCity: String) < city = newCity >fun incrementAge() < age++ >> fun main()

Функции области видимости не предоставляют никаких новых технических возможностей, но они могут сделать ваш код более кратким и читабельным.

Из-за того, что все функции области видимости имеют схожий характер, выбрать правильную для вашего конкретного случая может быть затруднительно. В основном выбор зависит от ваших намерений и от структуры вашего проекта. Ниже приведено подробное описание того, чем функции области видимости отличаются между собой, а также соглашение об их использовании.

Отличительные особенности

Поскольку функции области видимости очень похожи друг на друга, важно понимать чем они различаются. Между ними есть два основных различия:

  • Способ ссылки на контекстный объект
  • Возвращаемое значение.

Контекстный объект: this или it

Внутри лямбда-выражения, которое передается функции области видимости, объект контекста доступен по краткой ссылке, а не по его фактическому имени. Каждая функция области видимости использует один из двух способов доступа к объекту контекста: как лямбда-получатель ( this ) или как лямбда-аргумент ( it ). Оба предоставляют одинаковые возможности, поэтому опишем плюсы и минусы каждого для разных случаев и дадим рекомендации по их использованию.

fun main() < val str = "Hello" // this str.run < println("Длина строки: $length") //println("Длина строки: $") // делает то же самое > // it str.let < println("Длина строки: $") > > 
this

run , with и apply ссылаются на объект контекста как лямбда-получатель — по ключевому слову this . Следовательно, в их лямбдах объект доступен, как это было бы в обычных функциях класса. В большинстве случаев this можно опустить при доступе к элементам объекта-получателя, что сделает код короче. С другой стороны, если this опущено, то будет сложнее различить элементы-получатели с внешними объектами или функциями. Таким образом, наличие this рекомендуется для лямбд, которые в основном работают с членами объекта: вызывают его функции или присваивают свойства.

data class Person(var name: String, var age: Int = 0, var city: String = "") fun main() < val adam = Person("Adam").apply < age = 20 // то же самое, что и this.age = 20 или adam.age = 20 city = "London" >println(adam) > 
it

В свою очередь, let и also передают контекстный объект как аргумент в лямбду. Если имя аргумента не указано, то к объекту обращаются неявным способом при помощи ключевого слова it . it короче this и выражения с it более читабельны. Однако при вызове функций или свойств объекта у вас не будет доступа к такому неявному объекту как this . Следовательно, использовать контекстный объект it лучше, когда объект в основном используется в качестве аргумента для вызова функций. it также лучше, если вы используете несколько переменных в блоке кода.

import kotlin.random.Random fun writeToLog(message: String) < println("INFO: $message") >fun main() < fun getRandomInt(): Int < return Random.nextInt(100).also < writeToLog("Метод getRandomInt() сгенерировал значение $it") >> val i = getRandomInt() > 

Кроме того, когда вы передаете объект в качестве аргумента, вы можете указать пользовательское имя для этого объекта внутри области видимости.

import kotlin.random.Random fun writeToLog(message: String) < println("INFO: $message") >fun main() < fun getRandomInt(): Int < return Random.nextInt(100).also < value ->writeToLog("Метод getRandomInt() сгенерировал значение $value") > > val i = getRandomInt() > 

Возвращаемое значение

Функции области видимости также отличаются по значению, которое они возвращают:

  • apply и also возвращают объект контекста.
  • let , run и with возвращают результат лямбды.

Это отличие позволит выбрать правильную функцию в зависимости от того, что вы будете делать дальше в своем коде.

Контекстный объект

Функции apply и also возвращают объект контекста. Следовательно, с их помощью можно вызвать длинную цепочку функций относительно оригинального контекстного объекта. Такая цепочка функций известна как side steps.

fun main() < val numberList = mutableListOf() numberList.also < println("Заполнение списка") >.apply < add(2.71) add(3.14) add(1.0) >.also < println("Сортировка списка") >.sort() println(numberList) > 

Они также могут быть использованы совместно с ключевым словом return .

import kotlin.random.Random fun writeToLog(message: String) < println("Информация: $message") >fun main() < fun getRandomInt(): Int < return Random.nextInt(100).also < writeToLog("Метод getRandomInt() сгенерировал значение $it") >> val i = getRandomInt() > 
Результат лямбды

let , run и with возвращают результат лямбды. Таким образом, вы можете использовать их при присваивании переменной результата вычислений, либо использовать результат для последующего вызова цепочки операций и тд.

fun main() < val numbers = mutableListOf("one", "two", "three") val countEndsWithE = numbers.run < add("four") add("five") count < it.endsWith("e") >> println("Элементы в $countEndsWithE, которые заканчиваются на e.") > 

Кроме того, вы можете игнорировать возвращаемое значение и использовать функцию для создания временной области видимости для переменной.

fun main() < val numbers = mutableListOf("one", "two", "three") with(numbers) < val firstItem = first() val lastItem = last() println("Первый элемент: $firstItem, последний элемент: $lastItem") >> 

Функции

Чтобы помочь вам выбрать правильную функцию области видимости, ниже будет представлено их подробное описание и рекомендации по использованию. Во многих случаях они взаимозаменяемы, поэтому в примерах будет отражен общий стиль использования, а также соглашение по их применению.

let

Контекстный объект доступен в качестве аргумента ( it ). Возвращаемое значение — результат выполнения лямбды.

Если значение переменной вычислялось при помощи цепочки операций, то let позволяет использовать полученный результат для вызова одной или нескольких функций в блоке кода. Например, в следующем коде выполняется цепочка из двух операций, результат записывается в отдельную переменную, после чего она выводится на печать.

fun main() < val numbers = mutableListOf("one", "two", "three", "four", "five") val resultList = numbers.map < it.length >.filter < it >3 > println(resultList) > 

С функцией let этот код может быть переписан следующим образом:

fun main() < val numbers = mutableListOf("one", "two", "three", "four", "five") numbers.map < it.length >.filter < it >3 >.let < println(it) // при необходимости можно вызвать больше функций >> 

Если блок кода содержит одну функцию, где it является аргументом, то лямбда-выражение может быть заменено ссылкой на метод ( :: ):

fun main() < val numbers = mutableListOf("one", "two", "three", "four", "five") numbers.map < it.length >.filter < it >3 >.let(::println) > 

let часто используется для выполнения блока кода только с non-null значениями. Чтобы выполнить действия с non-null объектом, используйте оператор безопасного вызова ?. совместно с функцией let .

fun processNonNullString(str: String) <> fun main() < val str: String? = "Hello" //processNonNullString(str) // compilation error: str может быть null val length = str?.let < println("Вызов функции let() для $it") processNonNullString(it) // OK: 'it' не может быть null внутри конструкции '?.let < >' it.length > > 

Еще один вариант использования let — это введение локальных переменных с ограниченной областью видимости для улучшения читабельности кода. Чтобы определить новую переменную для контекстного объекта, укажите ее имя в качестве аргумента лямбды, чтобы ее можно было использовать вместо ключевого слова it .

fun main() < val numbers = listOf("one", "two", "three", "four") val modifiedFirstItem = numbers.first().let < firstItem ->println("Первый элемент в списке: '$firstItem'") if (firstItem.length >= 5) firstItem else "!" + firstItem + "!" >.toUpperCase() println("Первый элемент списка после изменений: '$modifiedFirstItem'") > 

with

Не является функцией-расширением. Контекстный объект передается в качестве аргумента, а внутри лямбда-выражения он доступен как получатель ( this ). Возвращаемое значение — результат выполнения лямбды.

Функцию with рекомендуется использовать для вызова функций контекстного объекта без предоставления результата лямбды. В коде with может читаться как» с этим объектом, сделайте следующее. «

fun main() < val numbers = mutableListOf("one", "two", "three") with(numbers) < println("'with' вызывает с аргументом $this") println("Список содержит $size элементов") >> 

Другой вариант использования with — введение вспомогательного объекта, свойства или функции которые будут использоваться для вычисления значения.

fun main() < val numbers = mutableListOf("one", "two", "three") val firstAndLast = with(numbers) < "Первый элемент списка - $," + " последний элемент списка - $" > println(firstAndLast) > 

run

Контекстный объект доступен в качестве получателя ( this ). Возвращаемое значение — результат выполнения лямбды.

run делает то же самое, что и with , но вызывается как let — как функция расширения контекстного объекта.

run удобен, когда лямбда содержит и инициализацию объекта, и вычисление возвращаемого значения.

class MultiportService(var url: String, var port: Int) < fun prepareRequest(): String = "Запрос по умолчанию" fun query(request: String): String = "Результат запроса: '$request'" >fun main() < val service = MultiportService("https://example.kotlinlang.org", 80) val result = service.run < port = 8080 query(prepareRequest() + " порт - $port") >// аналогичный код с использованием функции let(): val letResult = service.let < it.port = 8080 it.query(it.prepareRequest() + " порт - $") > println(result) println(letResult) > 

Помимо вызова run для объекта-получателя, вы можете использовать его как функцию без расширения. В этом случае run позволяет выполнить блок из нескольких операторов там, где это требуется.

fun main() < val hexNumberRegex = run < val digits = "0-9" val hexDigits = "A-Fa-f" val sign = "+-" Regex("[$sign]?[$digits$hexDigits]+") >for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) < println(match.value) >> 

apply

Контекстный объект доступен в качестве получателя ( this ). Возвращаемое значение — контекстный объект.

Используйте apply для такого блока кода, который не возвращает значение и в основном работает с членами объекта-получателя. Типичный способ использования функции apply — настройка объекта-получателя. Это всеравно что мы скажем “примени перечисленные настройки к объекту.”

data class Person(var name: String, var age: Int = 0, var city: String = "") fun main() < val adam = Person("Adam").apply < age = 32 city = "London" >println(adam) > 

Так как возвращаемое значение — это сам объект, то можно с легкостью включить apply в цепочки вызовов для более сложной обработки.

also

Контекстный объект доступен в качестве аргумента ( it ). Возвращаемое значение — контекстный объект.

also хорош для выполнения таких действий, которые принимают контекстный объект в качестве аргумента. То есть, эту функции следует использовать, когда требуется ссылка именно на объект, а не на его свойства и функции. Либо, когда вы хотите, чтобы была доступна ссылка на this из внешней области видимости.

Когда вы видите в коде also , то это можно прочитать как «а также с объектом нужно сделать следующее.»

fun main() < val numbers = mutableListOf("one", "two", "three") numbers .also < println("Элементы списка перед добавлением нового: $it") >.add("four") > 

Выбор функции

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

Функция Обращение к объекту Возвращаемое значение Является функцией-расширением
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 ).

import kotlin.random.* fun main() < val number = Random.nextInt(100) val evenOrNull = number.takeIf < it % 2 == 0 >val oddOrNull = number.takeUnless < it % 2 == 0 >println("четный: $evenOrNull, нечетный: $oddOrNull") > 

При добавлении в цепочку вызовов других функций после takeIf и takeUnless , не забудьте выполнить проверку на null или используйте оператор безопасного вызова ( ?. ), потому что их возвращаемое значение имеет тип nullable.

fun main() < val str = "Hello" val caps = str.takeIf < it.isNotEmpty() >?.toUpperCase() //val caps = str.takeIf < it.isNotEmpty() >.toUpperCase() //compilation error println(caps) > 

takeIf и takeUnless особенно полезны при совместном использовании с функциями области видимости. Хорошим примером является объединение их в цепочку с let для выполнения блока кода для объектов, которые соответствуют заданному предикату. Для этого вызовите takeIf для объекта, а затем вызовите let с оператором безопасного вызова ( ? ). Для объектов, которые не соответствуют предикату, takeIf возвращает null , а let не вызывается.

fun main() < fun displaySubstringPosition(input: String, sub: String) < input.indexOf(sub).takeIf < it >= 0 >?.let < println("Подстрока $sub находится в $input.") println("Начинается с индекса $it.") >> displaySubstringPosition("010000011", "11") displaySubstringPosition("010000011", "12") > 

Тот же самый код, но без использования функций из стандартной библиотеки, выглядит следующим образом:

fun main() < fun displaySubstringPosition(input: String, sub: String) < val index = input.indexOf(sub) if (index >= 0) < println("Подстрок $sub находится в $input.") println("Начинается с индекса $index.") >> displaySubstringPosition("010000011", "11") displaySubstringPosition("010000011", "12") > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *