У меня проблемы с пониманием lambda
.
В частности, такие вещи, как T.() -> R
в run()
, сбивают с толку.
public inline fun <T, R> T.run(block: T.() -> R): R = return block()
Вопросы
fun main() {
test {
this.replace("3","B")
}
}
fun test(block: String.(Int) -> Unit) {
"TEST".block(7)
}
В этом коде, в block parameter
, String.()
означает определить block
как extension function of the String class
? Итак, блок требует String value
при вызове?
и,
fun main() {
"A".test {
this.replace("3","B")
}
}
fun String.test(block: String.(Int) -> Unit) {
block(7)
}
При вызове block
в этом коде, почему нам не нужен receiver type
?
in the
block
parameter,String.()
means to defineblock
as an extension function of theString
class? Soblock
requiresString
value when calling?
Да. Блок фактически является функцией расширения класса String
: внутри блока this
является соответствующим экземпляром String
.
When calling the
block
in this code, why don't we need the receiver type?
Потому что уже есть неявный получатель. test()
сама по себе является функцией расширения String
, поэтому внутри тела test()
this
является строкой. Таким образом, вы можете вызывать любые методы String
, не уточняя их с помощью this.
, включая такие методы расширения, как block
.
Рассмотрим более простой случай с «настоящими» методами вместо методов расширения:
class C {
fun a() {
// …
}
fun b() {
a() // Implicit receiver
}
}
a()
— это метод класса C
, поэтому ему нужен экземпляр C
. Но b()
не нужно указывать экземпляр C
при вызове a()
, потому что у него уже есть собственный приемник.
Было написано мог как this.a()
, но в этом нет необходимости, как this
всегда является подразумеваемым получателем, если вы его не укажете. (Некоторым нравится явный this.
, но для меня это просто бессмысленный визуальный шум…)
Хотя методы расширения реализованы немного по-другому «под обложкой», синтаксис работает точно так же. Таким образом, вызов block()
во втором примере имеет неявный this.
.
In particular, things like T.() -> R in the run() is more confusing.
public inline fun <T, R> T.run(block: T.() -> R): R = block()
Здесь T
и R
относятся к общему типу, который может относиться к любому типу данных Int
, String
или Class
и т. д. Вы поняли.
block: T.() -> R
, это говорит о том, что block
должна быть extension
функцией типа T
, которая может возвращать любой тип R
, и это R
может быть чем угодно, Unit
, String
, Class
и т. д.
lambda
возвращает значение своего последнего выражения, поэтому любое выражение, которое находится в последней строке вашего lambda
, вернет его, т. е. имеет тип R
.
Теперь, когда мы используем этот метод run
для любого объекта, он дает нам тот же объект, что и получатель lambda
(this
) внутри лямбды, потому что наша lambda
является функцией расширения того же типа, для которого мы вызвали это.
var a = 1
val b = a.run {
this + 6
"incremented"
}
При использовании метода run
для a
наш универсальный тип T
становится Int
, а наш объект a
доступен внутри lambda
как this
, потому что теперь это функция расширения Int
.
В lambda
нашим последним выражением является "incremented"
, которое является String
, поэтому здесь наше R
становится типом String
. Поскольку метод run
возвращает это R
, значение переменной b
становится incremented
.
In this code, in the block parameter,
String.()
means to define block as an extension function of the String class? So block requiresString
value when calling?
Если block
является функцией extension
, вам не нужно передавать String
. Когда вы вызываете его на любом String
, он будет использовать это. Но если block
не является extension
функцией, вам придется ее передать.
fun test(block: String.(Int) -> Unit) {
"TEST".block(7)
}
.
fun String.test(block: String.(Int) -> Unit) {
block(7)
}
When calling the block in this code, why don't we need the receiver type?
В последнем метод test
является extension
функцией String
, которая доступна как this
в теле метода, и также доступны все функции String
, которые вы можете использовать с this
или без block
на объекте-получателе. Поскольку String
также является функцией расширения String
, к ней можно получить прямой доступ.
Между тем, в первом случае в теле метода нет объекта-приемника типа String
, поэтому вы должны явно вызывать его для T
.
благодарю вас. В конце концов, это стало возможным, потому что и
test()
, иblock
являются функциями расширения, верно? Таким образом, как в примере сreal methods
, иblock
, иtest()
являютсяmember functions
, которые являютсяextensions of the String class
, поэтому вы можете называтьblock
вtest() body
безreceiver object(String object)
. Потому что внутриthis
тела скрытоtest()
. Правильно??