我希望每个运行的函数都有一个很好的例子,让我们应用
我已经阅读了这篇文章,但仍然缺乏示例
Answers:
所有这些功能都用于切换当前功能/变量的范围。它们用于将属于在一起的事物放在一个位置(大多数情况下是初始化)。
这里有些例子:
run
-返回您想要的任何内容,并重新作用域范围内的变量 this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
现在,密码生成器的作用域为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)
}
上面的代码仅在苹果不为null时才将其添加到购物篮中。还要注意,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%基于编程/基于逻辑的示例时,那么我们处于更好的概念化位置。但这取决于权利:)
让,也应用,takeIf,takeUnlow是Kotlin中的扩展功能。
要了解这些功能,您必须了解Kotlin中的扩展功能和Lambda函数。
扩展功能:
通过使用扩展功能,我们可以在不继承类的情况下为类创建函数。
与C#和Gosu相似,Kotlin提供了使用新功能扩展类的功能,而不必继承该类或使用任何类型的设计模式(例如Decorator)。这通过称为扩展的特殊声明来完成。Kotlin支持扩展功能和扩展属性。
因此,要查找中是否只有数字String
,您可以创建如下所示的方法而无需继承String
类。
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
您可以使用上述扩展功能,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
这是版画true
。
Lambda函数:
Lambda函数就像Java中的Interface。但是在Kotlin中,lambda函数可以作为参数传递给函数。
例:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
您可以看到,该块是lambda函数,它作为参数传递。您可以像这样使用上面的功能,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
上面的功能将这样打印,
Block executed
true
我希望,现在您对扩展函数和Lambda函数有了一个了解。现在我们可以一一进入扩展功能。
让
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
以上功能中使用了T和R这两种类型。
T.let
T
可以是String类之类的任何对象。因此您可以对任何对象调用此函数。
block: (T) -> R
在let的参数中,您可以看到上面的lambda函数。同样,调用对象作为函数的参数传递。因此,您可以在函数内部使用调用类对象。然后它返回R
(另一个对象)。
例:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
在上面的示例中,让String作为其lambda函数的参数,并返回Pair。
同样,其他扩展功能也可以工作。
也
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
扩展函数also
将调用类作为lambda函数参数,但不返回任何内容。
例:
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)
}
您可以在上面的示例中看到在lambda函数内部直接调用的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
。
除非
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之间的区别也很有用,在Kotlin中应用,使用,使用,takeIf和takeUnless
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
}
如果您需要返回接收对象本身,使用的应用或还
根据我的经验,由于这些函数是内联语法糖,没有性能差异,因此,您应始终选择需要在lamda中编写最少代码量的函数。
为此,首先确定您是希望lambda返回其结果(选择run
/ let
)还是对象本身(选择apply
/ also
);那么在大多数情况下,当lambda是单个表达式时,请选择与该表达式具有相同的块函数类型的表达式,因为当它是接收器表达式时,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"
但是,当lambda由它们的混合物组成时,则取决于您,然后选择一个更适合上下文或您感觉更舒适的lambda。
另外,在需要解构时,请使用带有参数块功能的参数:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
这是JetBrains针对Java开发人员的Coursera Kotlin官方Kotlin课程中所有这些功能的简要比较: