Я хочу иметь хороший пример для каждого запуска функции, пусть, применимо и с
Я прочитал эту статью, но все еще не нашел примера
Я хочу иметь хороший пример для каждого запуска функции, пусть, применимо и с
Я прочитал эту статью, но все еще не нашел примера
Ответы:
Все эти функции используются для переключения объема текущей функции / переменной. Они используются для хранения вещей, которые принадлежат друг другу, в одном месте (в основном инициализации).
Вот некоторые примеры:
run - возвращает все, что вы хотите, и изменяет область видимости переменной, для которой он используется this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
Генератор паролей теперь rescoped , как thisи , следовательно , мы можем установить seed, hashи hashRepetitionsбез использования переменной.
generate()вернет экземпляр Password.
applyпохоже, но вернет this:
val generator = PasswordGenerator().apply {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
}
val pasword = generator.generate()
Это особенно полезно в качестве замены паттерна Builder и если вы хотите повторно использовать определенные конфигурации.
let- в основном используется, чтобы избежать нулевых проверок, но также может использоваться как замена run. Разница в том, что thisона по-прежнему будет такой же, как и раньше, и вы получите доступ к переменной с измененной областью видимости, используя it:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
Приведенный выше код добавит яблоко в корзину, только если он не нулевой. Также обратите внимание, что itтеперь это больше не является необязательным, поэтому вы не столкнетесь с NullPointerException здесь (иначе говоря, вам не нужно использовать ?.для доступа к его атрибутам)
also- используйте, когда хотите использовать apply, но не хотите затенятьthis
class FruitBasket {
private var weight = 0
fun addFrom(appleTree: AppleTree) {
val apple = appleTree.pick().also { apple ->
this.weight += apple.weight
add(apple)
}
...
}
...
fun add(fruit: Fruit) = ...
}
applyЗдесь используется тень this, так что this.weightэто относится к яблоку, а не к корзине с фруктами.
Примечание: я бессовестно взял примеры из своего блога
Есть еще несколько статей , как здесь , и здесь , которые стоит посмотреть.
Я думаю, это связано с тем, что вам нужно более короткое, более лаконичное в нескольких строках и избежать ветвления или проверки условных операторов (например, если не null, то сделайте это).
Мне нравится эта простая диаграмма, поэтому я связал ее здесь. Вы можете увидеть это из этого , как написано Себастьяно Готтардо.
Пожалуйста, также посмотрите таблицу, сопровождающую мое объяснение ниже.
Я думаю, что это как ролевой способ внутри вашего блока кода, когда вы вызываете эти функции + хотите ли вы вернуть себя (чтобы связать функции вызова или установить переменную результата и т.д.).
Выше то, что я думаю.
Давайте посмотрим примеры для всех здесь
1.) myComputer.apply { }означает, что вы хотите действовать в качестве главного действующего лица (вы хотите думать, что вы компьютер), и вы хотите вернуть себя (компьютер), чтобы вы могли
var crashedComputer = myComputer.apply {
// you're the computer, you yourself install the apps
// note: installFancyApps is one of methods of computer
installFancyApps()
}.crash()
Да, вы сами просто устанавливаете приложения, аварийно завершаете работу и сохранили себя в качестве справки, чтобы другие могли увидеть и что-то с ними сделать.
2.) myComputer.also {}означает, что вы полностью уверены, что вы не компьютер, вы посторонний человек, который хочет что-то с ним сделать, а также хочет, чтобы компьютер был возвращенным результатом.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
3.) with(myComputer) { }означает, что вы являетесь главным действующим лицом (компьютером), и вы не хотите, чтобы в результате вы вернулись.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
4.) myComputer.run { }означает, что вы являетесь главным действующим лицом (компьютером), и вы не хотите, чтобы в результате вы вернулись обратно.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
но это отличается with { }в очень тонком смысле от того, что вы можете связать вызовы, run { }как показано ниже
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
Это связано с run {}функцией расширения, но with { }это не так. Итак, вы вызываете, run { }и thisвнутри блока кода будет отражен тип объекта вызывающего объекта. Вы можете увидеть это для отличного объяснения разницы между run {}и with {}.
5.) myComputer.let { }означает, что вы посторонний, который смотрит на компьютер и хочет что-то с ним сделать, не заботясь о том, чтобы экземпляр компьютера был возвращен вам снова.
myComputer.let {
myGrandpa.installVirusOn(it)
}
Я склонен смотреть на alsoи letкак нечто внешнее, снаружи. Когда вы говорите эти два слова, вы как будто пытаетесь из-за чего-то действовать. letустановить вирус на этот компьютер и alsoвывести его из строя. Так что это решает часть того, актер вы или нет.
Что касается результата, то это явно есть. alsoвыражает, что это тоже другое дело, поэтому вы по-прежнему сохраняете доступность самого объекта. Таким образом он возвращает его как результат.
Все остальное ассоциируется с this. К тому же run/withявно не заинтересован в возврате объекта-себя. Теперь вы можете различить их всех.
Я думаю, что иногда, когда мы отходим от примеров, основанных на 100% программировании / логике, мы оказываемся в лучшем положении для осмысления вещей. Но это зависит правильно :)
let, также, apply, takeIf, takeUnless - это функции расширения в Kotlin.
Чтобы понять эти функции, вы должны понимать функции расширения и лямбда-функции в Kotlin.
Функция расширения:
Используя функцию расширения, мы можем создать функцию для класса без наследования класса.
Kotlin, подобно C # и Gosu, предоставляет возможность расширять класс с помощью новых функций без необходимости наследовать от класса или использовать какой-либо тип шаблона проектирования, такой как Decorator. Это делается с помощью специальных объявлений, называемых расширениями. Kotlin поддерживает функции расширения и свойства расширения.
Итак, чтобы найти только числа в String, вы можете создать метод, как показано ниже, без наследования Stringclass.
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
вы можете использовать указанную выше функцию расширения следующим образом:
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
который печатает true.
Лямбда-функции:
Лямбда-функции похожи на интерфейс в Java. Но в Kotlin лямбда-функции можно передавать как параметр в functions.
Пример:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
Как видите, блок является лямбда-функцией и передается в качестве параметра. Вы можете использовать указанную выше функцию следующим образом:
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
Вышеупомянутая функция будет напечатана следующим образом:
Block executed
true
Надеюсь, теперь вы получили представление о функциях расширения и лямбда-функциях. Теперь мы можем перейти к функциям расширения по очереди.
позволять
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
В приведенной выше функции используются два типа T и R.
T.let
Tможет быть любой объект, например класс String. поэтому вы можете вызывать эту функцию с любыми объектами.
block: (T) -> R
В параметре let вы можете увидеть указанную выше лямбда-функцию. Кроме того, вызывающий объект передается как параметр функции. Таким образом, вы можете использовать вызывающий объект класса внутри функции. затем он возвращает R(другой объект).
Пример:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
В приведенном выше примере let принимает String в качестве параметра своей лямбда-функции и возвращает в ответ Pair .
Таким же образом работает и другая функция расширения.
также
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
функция extension alsoпринимает вызывающий класс как параметр лямбда-функции и ничего не возвращает.
Пример:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
применять
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
То же, что и, но тот же вызывающий объект, переданный в качестве функции, поэтому вы можете использовать функции и другие свойства, не вызывая его или имя параметра.
Пример:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
В приведенном выше примере вы можете видеть функции класса String, непосредственно вызываемые внутри лямбда-функции.
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Пример:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
В приведенном выше примере numberбудет только строка, phoneNumberсоответствующая regex. В противном случае так и будет null.
takeUnless
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
Это противоположно takeIf.
Пример:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
numberбудет иметь строку, phoneNumberтолько если не соответствует regex. В противном случае так и будет null.
Вы можете просмотреть похожие ответы, которые полезны здесь, разница между kotlin также, apply, let, use, takeIf и takeUnless в Kotlin
phoneNumber. takeUnless{}вместо phoneNumber. takeIf{}.
Есть 6 различных функций обзора:
Я подготовил визуальную заметку, как показано ниже, чтобы показать различия:
data class Citizen(var name: String, var age: Int, var residence: String)
Решение зависит от ваших потребностей. Сценарии использования различных функций частично совпадают, поэтому вы можете выбирать функции на основе определенных соглашений, используемых в вашем проекте или группе.
Хотя функции области видимости позволяют сделать код более кратким, избегайте их чрезмерного использования: это может снизить читабельность кода и привести к ошибкам. Избегайте вложенных функций области видимости и будьте осторожны при их связывании: легко запутаться в текущем объекте контекста и его значении.
Вот еще одна диаграмма для выбора из https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84.

Некоторые условные обозначения следующие:
Также используется для дополнительных действий, не изменяющих объект, таких как ведение журнала или печать отладочной информации.
val numbers = mutableListOf("one", "two", "three")
numbers
.also { println("The list elements before adding new one: $it") }
.add("four")
Обычный случай применения - это конфигурация объекта.
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
Если вам нужно затенение, используйте run
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
Если вам нужно вернуть сам объект-получатель, используйте apply или также
По моему опыту, поскольку такие функции являются встроенным синтаксическим сахаром без разницы в производительности, вы всегда должны выбирать ту, которая требует написания наименьшего количества кода в lamda.
Для этого сначала определите, хотите ли вы, чтобы лямбда возвращала свой результат (выберите run/ let) или сам объект (выберите apply/ also); то в большинстве случаев, когда лямбда является одним выражением, выбирайте те, которые имеют тот же тип функции блока, что и это выражение, потому что, когда это выражение-получатель, thisего можно опустить, когда оно является выражением параметра, itкороче, чем this:
val a: Type = ...
fun Type.receiverFunction(...): ReturnType { ... }
a.run/*apply*/ { receiverFunction(...) } // shorter because "this" can be omitted
a.let/*also*/ { it.receiverFunction(...) } // longer
fun parameterFunction(parameter: Type, ...): ReturnType { ... }
a.run/*apply*/ { parameterFunction(this, ...) } // longer
a.let/*also*/ { parameterFunction(it, ...) } // shorter because "it" is shorter than "this"
Однако, когда лямбда состоит из их комбинации, тогда вам решать, какая из них лучше соответствует контексту или с которой вам удобнее.
Также используйте те, у которых есть функция блока параметров, когда требуется деконструкция:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Вот краткое сравнение всех этих функций из официального курса JetBrains по Kotlin на Coursera Kotlin для разработчиков Java :
