I've looked at the source code of let function:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
i clearly understand that using it keyword inside of the block (code) i'm sending to let function will refer to the object that called the let function. But on which object does this keyword will refer if i will use it inside the block (code). The way i see it, it should be the same object that it refer to, but i'm not sure. My computer gone bad so i can't test it by myself. Can someone not only give me the answer but also will explain me the logic behind the this keyword in the situation above?
example of code:
val p:Preson = Person()
p.let{**this**}
CodePudding user response:
this
in the let
lambda (block) means the same thing as it does outside the let
lambda. The let
lambda does not suddenly "change" what this
means.
For example:
class Foo {
fun foo() {
// here, "this" refers to the receiver of foo(), i.e. some instance of Foo
1.let {
// here, "it" refers to 1,
// and "this" refers to the receiver of foo() too
}
}
}
fun main() {
// here, "this" does not mean anything, main() has no receiver
2.let {
// here, "it" refers to 2,
// and "this" does not mean anything either
}
}
This is because this
refers to the receiver of a function, but the function type of the parameter that let
takes has no receiver:
public inline fun <T, R> T.let(block: (T) -> R): R {
^^^^^^^^
Compare that to run
, which is very similar to let
, except its receiver as the receiver of the block
parameter:
public inline fun <T, R> T.run(block: T.() -> R): R {
return this.block() // instead of block(this) like let
}
which means that:
1.let {
// here, "this" refers to 1
// "it" does not mean anything because the function type T.() -> R has no parameters
}
Another example that uses both this
and it
:
fun usesBoth(x: Int, y: String, block: Int.(String) -> Unit) {
x.block(y)
}
If you call usesBoth
with a lambda,
usesBoth(1, "x") {
println(this) // prints 1
println(it) // prints x
}
this
in the lambda would refer to 1, because block
is called on x
in usesBoth
, and it
would refer to "x", because y
is passed to block
as a parameter.
CodePudding user response:
this
is just one of parameters (called a "receiver") that could be passed to the lambda by a calling code. If lambda doesn't support a receiver, as in the let
example, this
will be just this
of the enclosing code:
class Foo {
fun foo() {
val p = Person()
p.let{ this } // `this` is `Foo`
}
}
If lambda has a receiver then it depends what will be passed to it as this
. For example, a very similar function to let
, but passing the object as this
is apply
:
class Foo {
fun foo() {
val p = Person()
p.apply{ this } // `this` is `Person`
}
}
Of course, this
doesn't have to be the object that we invoked the function on. It could be just any object, it depends on what the function will pass to the lambda. For example, in Kotlin stdlib we have a buildString()
function that instantiates a StringBuilder
, runs the lambda passing the StringBuilder
as this
and then returns a string that was built by the lambda. We use this function like this:
val s = buildString {
// `this` is `StringBuilder`
append("hello") // implicit this.append()
append("world")
}
By looking into buildString()
signature we can see that the lambda receives StringBuilder
as its receiver:
public inline fun buildString(builderAction: StringBuilder.() -> Unit): String {
You can read more about lambdas with receivers in the documentation: https://kotlinlang.org/docs/lambdas.html