Cómo crear callbacks de funciones anidadas como anko – syntax DSL Kotlin

Al igual que con anko puedes escribir funciones de callback como esta:

alert { title = "" message = "" yesButton { toast("Yes") } noButton { toast("No") } } 

¿Cómo puedo crear una function anidada como esa? Intenté crearlo como a continuación, pero parece que no funciona.

 class Test { fun f1(function: () -> Unit) {} fun f2(function: () -> Unit) {} } 

Ahora, si uso esto con la function de extensión,

 fun Context.temp(function: Test.() -> Unit) { function.onSuccess() // doesn't work } 

Llamar esto desde la actividad:

 temp { onSuccess { toast("Hello") } } 

No funciona Todavía me faltan algunos conceptos básicos aquí. ¿Alguien puede guiar aquí?

Kotlin DSLs

Kotlin es ideal para escribir sus propios lenguajes específicos de dominio , también denominados constructores de tipo seguro . Anko es uno de los ejemplos que usa tales DSL. La function de idioma principal que debe entender aquí se denomina "Literales de function con receptor" , que utilizó aquí: Test.() -> Unit

Literales de funciones con receptor: conceptos básicos

Kotlin admite el concepto de "literales de funciones con receptores". Esto nos permite llamar a methods en el receptor de la function literal en su cuerpo sin ningún calificador específico . Esto es muy similar a las funciones de extensión , donde también es posible acceder a los miembros del object receptor dentro de la extensión.

Un ejemplo simple es una de las funciones más geniales de la biblioteca estándar de Kotlin: apply

 public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this } 

Como puede ver, se espera que tal literal de function con receptor sea un block arguments. Este bloque simplemente se ejecuta y el receptor (que es una instancia de T ) se devuelve. En acción, esto se ve de la siguiente manera:

 StringBuilder("Hello ").apply { append("Kotliner") append("! ") append("How are you doing?") }.toString()) 

Usamos StringBuilder como el receptor e invocar apply en él. El block , envuelto en {} , la expresión lambda, hace uso de no tener que usar calificadores adicionales y simplemente usa append here, una function definida en StringBuilder .

Literales de function con receptor – en DSL

Si observa este ejemplo, tomado de la documentation, verá esto en acción:

 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 } 

La function html() espera tal function literal con receptor con HTML como receptor. En el cuerpo de la function puede ver cómo se usa: se crea una instancia de HTML y se init .

Beneficio

Quien llama a una function de order superior que espera una function literal con receptor (como html() ) puede usar cualquier function y function HTML sin calificadores adicionales (como por ejemplo, por ejemplo), como puede ver en la llamada:

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

Tu ejemplo

Creé un ejemplo simple de lo que quería tener:

 class Context { fun onSuccess(function: OnSuccessAction.() -> Unit) { OnSuccessAction().function(); } class OnSuccessAction { fun toast(s: String) { println("I'm successful <3: $s") } } } fun temp(function: Context.() -> Unit) { Context().function() } fun main(args: Array<String>) { temp { onSuccess { toast("Hello") } } } 

En su alerta de ejemplo, la function devuelve alguna class, por ejemplo, Alerta. También esta function toma como parámetro la function literal con el receptor

En su ejemplo, debe hacer que onSuccess sea el método miembro de su class Test, y su function temp debería devolver la instancia de la class Test sin invocarlo. Pero para que se invoque un brindis como en su deseo, tiene que ser una function miembro de cualquier class devuelta por onSuccess

Creo que no entiendes exactamente cómo funcionan los literales funcionales con el receptor. Cuando te diviertes (algo: A. () -> Unidad) significa que este "algo" es la function miembro de la class A.

Asi que

Puedes mirar mi publicación en el blog: Cómo hacer una pequeña DSL para AsyncTask

  • Prueba unitaria de la function de extensión de Kotlin en las classs de Android SDK
  • No se puede acceder a EditText u otros componentes de la interfaz de usuario con Kotlin
  • ¿Cómo crear un button en Kotlin que abre una nueva actividad (Android Studio)?
  • Android Kotlin Extension super calling
  • Quiero agregar mi versión de kotlin al proyecto Gradel
  • La function Kotlin requiere nada, pero se define como un tipo diferente
  • Hacer clic en una vista de text para cambiar el fragment
  • Clases de Kotlin sin llaves
  • Obligatorio <Objeto> y encontrado <Objeto>?
  • En Kotlin Lenguaje de progtwigción importancia de varargs en términos de usos