Правильное понимание лямбды T.()

У меня проблемы с пониманием 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?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
37
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

in the block parameter, String.() means to define block as an extension function of the String class? So block requires String 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..

благодарю вас. В конце концов, это стало возможным, потому что и test(), и block являются функциями расширения, верно? Таким образом, как в примере с real methods, и block, и test() являются member functions, которые являются extensions of the String class, поэтому вы можете называть block в test() body без receiver object(String object). Потому что внутри this тела скрыто test(). Правильно??

ybybyb 07.05.2022 19:49

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 requires String 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.

Другие вопросы по теме