Kotlin中的“with”是什么意思?

36

我已经读了3遍有关它的文档,但仍然不知道它是做什么的。能否有人像我5岁一样简单易懂地解释一下?这是我的使用方式:

fun main(args: Array<String>) {
    val UserModel = UserModel()
    val app = Javalin.create().port(7000).start()

    with (app) {
        get("/users") {
            context -> context.json(UserModel)
        }
    }
}

4
哇,你的问题让我意识到没有一个好的文档解释 with 是如何工作的 - 它是一个函数,而不是一个关键字!所以我写了一个问题并同时回答它,也许这会给已有的答案增加一些价值。参见:Kotlin 中的“接收者”是什么? - F. George
5个回答

68

with被用于访问对象的成员和方法,而无需每次访问时都引用该对象。它主要用于缩写您的代码。(大多数情况)。在构造对象时经常使用它:

// Verbose way, 204 characters:
var thing = Thingummy()
thing.component1 = something()
thing.component2 = somethingElse()
thing.component3 = constantValue
thing.component4 = foo()
thing.component5 = bar()
parent.children.add(thing)
thing.refcount = 1

// Terse way, 182 characters:
var thing = Thingummy()
with(thing) {
  component1 = something()
  component2 = somethingElse()
  component3 = constantValue
  component4 = foo()
  component5 = bar()
  parent.children.add(this)
  refcount = 1
}

1
这也是一个很好的时机来指出,这鼓励不可变性;第二个 var thing 应该变成 val thing 并保证它永远不会改变,除非你想要改变它。 - Ky -
3
简洁又直接。我个人认为像这样的回答比啰嗦的回答更值得关注。因为 PO 明确要求“像我五岁一样解释”。并不是要贬低其他答案,但可以说简化问题的能力意味着你已经理解了关键点。 - Farid
3
@Supuhstar,“不可变性”与此无关。 val仅表示您无法重新分配。这并不意味着您不能更改状态。如果第二个“thing”可以是val,那么第一个也可以。 - Frank Neblung
1
我认为这个例子没有从with块返回实际的Thingummy对象。this应该是lambda中的最后一行。 - moffeltje
with 和作用域函数的主要区别是什么? - Bitwise DEVS
显示剩余2条评论

25

这段文档描述了 with 函数:

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

使用给定的接收者调用指定的函数块,并将其结果返回。

我的理解是,它调用一个函数(即 block),在 block 的范围内,thisreceiver。 无论 block 返回什么都将作为返回类型。

本质上,这是一种提供隐式 this 并能够从中返回任何结果的方法。

以下是示例:

val rec = "hello"
val returnedValue: Int = with(rec) {
  println("$this is ${length}")
  lastIndexOf("l")
}

在这种情况下,rec是函数调用的接收者——block范围内的this$lengthlastIndexOf 都是在接收者上调用的。
返回值可以看作是一个Int,因为它是body中最后一个方法调用——也就是签名中通用类型参数R

那么这只是一个工具,用于指定你想要进入的作用域? - Vlady Veselinov
1
@VladyVeselinov 基本上是这样的。还有一些其他的作用域函数可以帮助您编写更清晰的代码。您可以在yole/kotlin-style-guide GitHub存储库中查看一些示例和关于一般使用模式的讨论。 - mkobit

7
< p > with 的定义:

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

实际上,它的实现非常简单:该 blockreceiver 上执行,适用于任何类型:

receiver.block() //that's the body of `with`

这里需要提到的重要参数类型是 T.() -> R,它被称为带接收者的函数字面值。实际上,它是一种能够在不使用任何其他限定符的情况下访问接收者成员的lambda
在你的例子中,with 接收者 app 中的 context 就是这样被访问的。
除了像 withapply 这样的 stdlib 函数之外,这种功能也使 Kotlin 成为编写特定领域语言的绝佳选择,因为它允许创建作用域,在该作用域内可以访问某些功能。

2
val citizen2 = Citizen("Tom", 24, "Washington")

val age = with(citizen2) {
    println("$name -  $age  $residence ")
    age = this.age + age
    residence = "Florida"
    age+10 // returns 58
}
println("${citizen2.name} -  ${citizen2.age} - $age - ${citizen2.residence}  ")

data class Citizen(var name: String, var age: Int, var residence: String)

输出:

Tom -  24  Washington 
Tom -  48 - 58 - Florida

请注意:

  • 我们可以使用this.ageage 访问公民(接收者对象)的 age 属性。
  • with() 函数中的 lambda 的最后一行(在本例中为 age + 10)将被返回。

0

With 用于对对象应用多个操作或访问对象的方法,例如在此示例中,我们正在访问 String 的 capitalize() 扩展方法。

data class Person(val name:String)
fun main(){
   val person = Person("john doe")
    with(person) {
       println(name.capitalize()) // output John Doe
    }

}

在底层,with 是一个高阶函数。这里我们使用 Person 名称调用 capitalize()。我们实际上不需要 'this',因为它是隐式的,可以被移除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接