Объяснение необходимо для использования Kotlin функции приемника для DSL - PullRequest
1 голос
/ 13 апреля 2020

Я пытаюсь понять следующий код ( источник ).

class HTML {
    fun body() { ... }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()  // create the receiver object
    html.init()        // pass the receiver object to the lambda
    return html
}

html {       // lambda with receiver begins here
    body()   // calling a method on the receiver object
}

Что я действительно не могу gr asp это строка

html.init() // pass the receiver object to the lambda

Что здесь происходит?

Может кто-нибудь объяснить простыми словами, что здесь происходит?

Ответы [ 2 ]

3 голосов
/ 13 апреля 2020

Во-первых, давайте немного упростим этот пример и посмотрим, в чем проблема.

Мы могли бы построить функцию html следующим образом:

fun html(init: (HTML) -> Unit): HTML {
    val html = HTML()
    init(html)
    return html
}

Это было бы проще gr asp (сначала), потому что мы просто передаем обычный однопараметрический параметр лямбда к функции html.

Но теперь сайт вызова не похож на конструктор:

html { it: HTML -> // just for clarity     
    it.body() // not very nice
}

Не было бы неплохо, если бы мы могли вызвать body() внутри html без it? Это возможно! Все, что нам нужно, это лямбда с приемником.

fun html(init: HTML.() -> Unit): HTML { // <-- only this changed
    val html = HTML()
    init(html)
    return html
}

Посмотрите, как html передается init в качестве аргумента, как раньше? Конечно, мы можем также вызвать его так: html.init(), как показано в примере. Экземпляр HTML становится this внутри блока лямбды.

Теперь мы можем сделать это:

html {      
   this.body()
}

Поскольку this можно опустить, мы приходим сюда :

html {      
   body()
}

Итак, в итоге лямбды с получателями делают код более лаконичным и позволяют нам использовать хороший синтаксис компоновщика.

1 голос
/ 13 апреля 2020

Вот пошаговое объяснение:

1. Создание функции, тип приемника лямбда.

fun html(init: HTML.() -> Unit): HTML {

здесь функция html принимает параметр init типа HTML.() -> Unit, т. Е. Указывает, что это приемник HTML и может вызываться только с помощью реального HTML объекта. И : HTML указывает, что функция, очевидно, возвращает HTML объект.

2. вызов init в html

html.init()

Здесь функция init () вызывается как получатель HTML реальным HTML объектом.


Достаточно формального разговора. Вот что такое получатель:

Итак, если вы помните функцию расширения, определенную как fun A.myFun(...): ReturnType {}, в этом случае вы получите переменную this, которая действует как был вызван экземпляр типа A.

Аналогично, лямбда-получатель выдает переменную this внутри нее,

В конкретном примере:

class A {
    fun thisCanBeCalledByAInstance() {
        println("I've got called")
    }

}

fun main() {
    val receiver: A.() -> Unit = { // this: A
        thisCanBeCalledByAInstance() // prints: I've got called
        // or traditional way this.thisCanBeCalledByAInstance()
    }

    val a: A = A()
    a.receiver()
}

Здесь Вы были в состоянии вызвать метод (функцию) из экземпляра A, даже если это был лямбда, потому что это был получатель.

PS: Для простого языка вы можете подумать html .init () как init (html), но html не является параметром, а вместо этого работает как this vaiable внутри лямбды

Вот почему вы смогли вызвать body() для этого лямбда, потому что вы неявно звонили this.body(), а this пришел от html.init() html объекта.

...