Poniendo un lambda genérico en un map

Ok, entonces el siguiente código es un sistema de events que hace lo siguiente:

  1. Asigna un ID integer a una expresión lambda
  2. Pone la identificación de lambda en el set mutable de un evento
  3. Asigna el ID integer a la expresión lambda
  4. Devuelve la identificación (se puede usar más tarde para eliminar events de la lambda)

El código es el siguiente:

class EventHandler { companion object { val handlers = HashMap<KClass<out Event>, MutableSet<Int>>() val idMap = HashMap<Int, (Event) -> Unit>(); /** * @param event Class of the event you are registering * @param handler What to do when the event is called */ fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit): Int { var id: Int = 0; while(idMap[id] != null) { id++; } var list = handlers.getOrPut(event, {mutableSetOf()}); list.add(id); idMap[id] = handler; return id; } } } 

El uso previsto de este método sería algo como esto:

 EventHandler.register(ChatEvent::class) { onChat -> println(onChat.message) } 

Hay un error en la siguiente línea: idMap[id] = handler;

El error se debe a que el controller es de tipo (T) -> Unit , aunque debe ser (Event) -> Unit para agregarlo a idMap . Aunque dije que T debería extender Event cuando lo creé, entonces esto no debería ser un problema. ¿Alguien sabe por qué sucede esto si hay una solución?

El problema proviene del hecho de que idMap toma una function que recibe un Event , cualquier tipo de Event . Luego intenta registrar una function que toma una subclass específica de Event , que, cuando la vuelve a extraer, el comstackdor no podrá decir qué posible subclass recibe. Sí, está almacenando el tipo específico en su otro map, pero el comstackdor no puede usar eso.

No creo que puedas crear el sistema de mapeo que quieras crear. No sin algunas capas más de indirección o abstracción …

El motivo por el que obtiene este error lo explica bien @ jacob-zimmerman en su respuesta : (T) -> Unit no es un subtipo de (Event) -> Unit (al contrario, es un supertipo).

Puede hacer un downcast sin marcar para el tipo de function requerida:

 idMap[id] = handler as (Event) -> Unit 

Pero antes de invocar dicho controller, debe verificar que un evento tenga un tipo que el manejador pueda aceptar, por ejemplo, al consultar el controller desde el map según el tipo de evento:

 fun invoke(event: Event) { val kclass = event.javaClass.kotlin val eventHandlers = handlers[kclass]?.map { idMap[it]!! } ?: return eventHandlers.forEach { it.invoke(event) } } 

Descubrí una implementación:

 class Handlers<T: Event> { val backingList = ArrayList<(T) -> Unit>() fun add(handler: (T) -> Unit) { backingList.add(handler) } fun remove(handler: (T) -> Unit) { backingList.remove(handler) } fun handleEvent(event: T) { backingList.forEach { handle -> handle(event) } } } class HandlerMap { val handlerMap = HashMap<KClass<out Event>, Handlers<out Event>>() fun <T : Event> register(event: KClass<T>, handler: (T) -> Unit) { val list: Handlers<T> if(!handlerMap.containsKey(event)) { list = Handlers<T>() handlerMap.put(event, list) } else { list = handlerMap.get(event) as Handlers<T> } list.add(handler) } fun <T: Event> getHandlers(event: KClass<T>): Handlers<T> { return handlerMap.get(event) as Handlers<T> } } 

Tiene que hacer algo de casting debido a que el map está abierto, pero el sistema está cerrado, por lo que se garantiza que el object Handlers sea ​​exactamente del tipo correcto. Debido al cambio en la implementación, estaba bastante seguro de que ya no te importaba el índice / "id", pero probé una implementación con él, y estuvo bien; no hay problemas reales para ponerlo si lo quieres.

  • Configuración de oyentes de Android en Kotlin: uso del retorno en lambdas
  • ¿Por qué Kotlin no puede inferir el siguiente argumento lambda (después de la conversión de Java -> Kotlin)?
  • Android Kotlin no puede usar list.sort () con lambda
  • kotlin android - Diálogo personalizado con patrón de construcción y Java 8 lambda
  • ¿Pasar lambdas a Observable.subscribe en kotlin dará como resultado pérdidas de memory?
  • cómo descartar DialogFragment de lambda especificado en el llamador Kotlin
  • ¿Cómo pasar arguments a la function lambda anónima en Kotlin?
  • Kotlin: lambda nunca comstack
  • ¿Cuál es el propósito de Lambda's con Receiver?
  • ¿Cómo funciona el generador htmlx de kotlin exactamente debajo del capó?
  • Tienda lambda en una variable en kotlin