Lov*_*vis 86
所有这些函数都用于切换当前函数/变量的范围.它们用于将属于一起的东西保存在一个地方(主要是初始化).
这里有些例子:
run - 返回您想要的任何内容,并重新调整其使用的变量范围 this
val password: Password = PasswordGenerator().run {
seed = "someString"
hash = {s -> someHash(s)}
hashRepetitions = 1000
generate()
}
Run Code Online (Sandbox Code Playgroud)
密码生成器现在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()
Run Code Online (Sandbox Code Playgroud)
这对于Builder模式的替代特别有用,并且如果您想重新使用某些配置.
let- 主要用于避免空检查,但也可以用作替代run.区别在于,它this仍然与以前相同,您可以使用it以下方法访问重新定义的变量:
val fruitBasket = ...
apple?.let {
println("adding a ${it.color} apple!")
fruitBasket.add(it)
}
Run Code Online (Sandbox Code Playgroud)
上面的代码只有在它不为空时才会将苹果添加到购物篮中.另请注意,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) = ...
}
Run Code Online (Sandbox Code Playgroud)
apply在这里使用阴影this,所以这this.weight将指的是苹果,而不是水果篮.
注意:我无耻地从我的博客中获取了示例
hax*_*por 44
我认为这取决于你需要更短,更简洁的几行,并避免分支或条件语句检查(如果不是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()
Run Code Online (Sandbox Code Playgroud)
是的,你自己只是安装应用程序,自己崩溃,并保存自己作为参考,以允许其他人看到和做一些事情.
2.)myComputer.also {}意味着你完全确定你不是计算机,你是一个想要用它做某事的局外人,并且还希望计算机作为返回的结果.
var crashedComputer = myComputer.also {
// now your grandpa does something with it
myGrandpa.installVirusOn(it)
}.crash()
Run Code Online (Sandbox Code Playgroud)
3.)with(myComputer) { }意味着你是主要演员(计算机),你不希望自己回来.
with(myComputer) {
// you're the computer, you yourself install the apps
installFancyApps()
}
Run Code Online (Sandbox Code Playgroud)
4.)myComputer.run { }意味着你是主要演员(计算机),你不希望自己回来.
myComputer.run {
// you're the computer, you yourself install the apps
installFancyApps()
}
Run Code Online (Sandbox Code Playgroud)
但它与with { }非常微妙的意义不同,你可以run { }像下面这样链接电话
myComputer.run {
installFancyApps()
}.run {
// computer object isn't passed through here. So you cannot call installFancyApps() here again.
println("woop!")
}
Run Code Online (Sandbox Code Playgroud)
这是由于run {}扩展功能,但with { }不是.因此,您调用run { }并this在代码块内部将反映到调用者类型的对象.您可以看到这一点,以获得run {}和之间区别的出色解释with {}.
5.)myComputer.let { }意味着你是一个看着电脑的外人,想要对它做些什么,而不用担心计算机实例会再次返回给你.
myComputer.let {
myGrandpa.installVirusOn(it)
}
Run Code Online (Sandbox Code Playgroud)
我倾向于看also和let为一些东西,是外部的,外部的.每当你说出这两个词时,就像你试图采取行动一样.let在此计算机上安装病毒,并also使其崩溃.所以这就说明了你是不是演员.
对于结果部分,它显然在那里.also表达它也是另一回事,所以你仍然保留对象本身的可用性.因此它返回它作为结果.
其他一切都与之相关this.另外run/with显然对返回对象 - 自我回归不感兴趣.现在你可以区分所有这些.
我认为有时当我们离开100%基于编程/逻辑的例子时,我们就能更好地概念化事物.但这取决于正确的:)
oiy*_*yio 18
有 6 种不同的作用域函数:
我准备了一个视觉笔记,如下所示,以显示差异:
data class Citizen(var name: String, var age: Int, var residence: String)
Run Code Online (Sandbox Code Playgroud)
决定取决于您的需求。不同功能的用例重叠,因此您可以根据您的项目或团队中使用的特定约定来选择功能。
尽管作用域函数是使代码更简洁的一种方式,但请避免过度使用它们:它会降低代码的可读性并导致错误。避免嵌套作用域函数并在链接它们时小心:很容易混淆当前上下文对象和 this 或 it 的值。
这是从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")
Run Code Online (Sandbox Code Playgroud)
apply的常见情况是对象配置。
val adam = Person("Adam").apply {
age = 32
city = "London"
}
println(adam)
Run Code Online (Sandbox Code Playgroud)
如果您需要阴影,请使用运行
fun test() {
var mood = "I am sad"
run {
val mood = "I am happy"
println(mood) // I am happy
}
println(mood) // I am sad
}
Run Code Online (Sandbox Code Playgroud)
如果您需要返回接收对象本身,使用的应用或还
Den*_*nis 10
我必须承认,乍一看差异并不那么明显,除其他外,因为这 5 个功能通常是可以互换的。这是我的理解:
APPLY -> 使用这些属性初始化一个对象并等待该对象
val paint = Paint().apply {
this.style = Paint.Style.FILL
this.color = Color.WHITE
}
Run Code Online (Sandbox Code Playgroud)
LET -> 隔离一段代码并等待结果
val result = let {
val b = 3
val c = 2
b + c
}
Run Code Online (Sandbox Code Playgroud)
或者
val a = 1
val result = a.let {
val b = 3
val c = 2
it + b + c
}
Run Code Online (Sandbox Code Playgroud)
或者
val paint: Paint? = Paint()
paint?.let {
// here, paint is always NOT NULL
// paint is "Paint", not "Paint?"
}
Run Code Online (Sandbox Code Playgroud)
ALSO -> 同时执行2个操作并等待结果
var a = 1
var b = 3
a = b.also { b = a }
Run Code Online (Sandbox Code Playgroud)
WITH -> 使用此变量/对象执行某些操作,并且不等待结果(不允许链接)
with(canvas) {
this.draw(x)
this.draw(y)
}
Run Code Online (Sandbox Code Playgroud)
RUN -> 使用此变量/对象执行某些操作,并且不等待结果(允许链接)
canvas.run {
this.draw(x)
this.draw(y)
}
Run Code Online (Sandbox Code Playgroud)
或者
canvas.run {this.draw(x)}.run {this.draw(x)}
Run Code Online (Sandbox Code Playgroud)
让,也应用,takeIf,takeUnlow是Kotlin中的扩展功能。
要了解这些功能,您必须了解Kotlin中的扩展功能和Lambda函数。
扩展功能:
通过使用扩展功能,我们可以在不继承类的情况下为类创建函数。
与C#和Gosu相似,Kotlin提供了使用新功能扩展类的功能,而不必继承该类或使用任何类型的设计模式(例如Decorator)。这是通过称为扩展的特殊声明完成的。Kotlin支持扩展功能和扩展属性。
因此,要查找中是否只有数字String,您可以创建如下所示的方法而无需继承String类。
fun String.isNumber(): Boolean = this.matches("[0-9]+".toRegex())
Run Code Online (Sandbox Code Playgroud)
您可以使用上述扩展功能,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber)
Run Code Online (Sandbox Code Playgroud)
这是版画true。
Lambda函数:
Lambda函数就像Java中的Interface。但是在Kotlin中,lambda函数可以作为参数传递给函数。
例:
fun String.isNumber(block: () -> Unit): Boolean {
return if (this.matches("[0-9]+".toRegex())) {
block()
true
} else false
}
Run Code Online (Sandbox Code Playgroud)
您可以看到,该块是lambda函数,它作为参数传递。您可以像这样使用上面的功能,
val phoneNumber = "8899665544"
println(phoneNumber.isNumber {
println("Block executed")
})
Run Code Online (Sandbox Code Playgroud)
上面的功能将这样打印,
Block executed
true
Run Code Online (Sandbox Code Playgroud)
我希望,现在您对扩展函数和Lambda函数有了一个了解。现在我们可以一一进入扩展功能。
让
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
Run Code Online (Sandbox Code Playgroud)
以上功能中使用了两种类型的T和R。
T.let
Run Code Online (Sandbox Code Playgroud)
T可以是String类之类的任何对象。因此您可以对任何对象调用此函数。
block: (T) -> R
Run Code Online (Sandbox Code Playgroud)
在let的参数中,您可以看到上面的lambda函数。同样,调用对象作为函数的参数传递。因此,您可以在函数内部使用调用类对象。然后它返回R(另一个对象)。
例:
val phoneNumber = "8899665544"
val numberAndCount: Pair<Int, Int> = phoneNumber.let { it.toInt() to it.count() }
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,让String作为其lambda函数的参数,然后返回Pair。
同样,其他扩展功能也可以工作。
也
public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
Run Code Online (Sandbox Code Playgroud)
扩展函数also将调用类作为lambda函数参数,但不返回任何内容。
例:
val phoneNumber = "8899665544"
phoneNumber.also { number ->
println(number.contains("8"))
println(number.length)
}
Run Code Online (Sandbox Code Playgroud)
应用
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Run Code Online (Sandbox Code Playgroud)
与函数相同,但传递的是相同的调用对象,因此您可以使用函数和其他属性,而无需调用它或参数名称。
例:
val phoneNumber = "8899665544"
phoneNumber.apply {
println(contains("8"))
println(length)
}
Run Code Online (Sandbox Code Playgroud)
您可以在上面的示例中看到在lambda函数内部直接调用的String类的功能。
takeIf
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else null
Run Code Online (Sandbox Code Playgroud)
例:
val phoneNumber = "8899665544"
val number = phoneNumber.takeIf { it.matches("[0-9]+".toRegex()) }
Run Code Online (Sandbox Code Playgroud)
在上述示例中number,phoneNumber只有一个字符串与匹配regex。否则,它将为null。
除非
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? = if (!predicate(this)) this else null
Run Code Online (Sandbox Code Playgroud)
与takeIf相反。
例:
val phoneNumber = "8899665544"
val number = phoneNumber.takeUnless { it.matches("[0-9]+".toRegex()) }
Run Code Online (Sandbox Code Playgroud)
numberphoneNumber如果不匹配,将只有一个字符串regex。否则,它将为null。
您可以查看类似的答案,这在Kotlin之间的区别也很有用,在Kotlin中应用,使用,使用,takeIf和takeUnless
根据我的经验,由于此类函数是内联语法糖,没有性能差异,因此您应该始终选择需要在 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"
Run Code Online (Sandbox Code Playgroud)
然而,当 lambda 由它们的组合组成时,您就可以选择更适合上下文或您感觉更舒服的那个。
另外,需要解构时,请使用带参数块功能的:
val pair: Pair<TypeA, TypeB> = ...
pair.run/*apply*/ {
val (first, second) = this
...
} // longer
pair.let/*also*/ { (first, second) -> ... } // shorter
Run Code Online (Sandbox Code Playgroud)
以下是 JetBrains 在 Coursera Kotlin for Java Developers上的官方 Kotlin 课程中对所有这些函数的简要比较:

| 归档时间: |
|
| 查看次数: |
9775 次 |
| 最近记录: |