web-dev-qa-db-fra.com

Kotlin: Utiliser un lambda à la place d'une interface fonctionnelle?

Dans Java nous pouvons le faire Events.handler(Handshake.class, hs -> out.println(hs));

En Kotlin cependant, j'essaie de reproduire le comportement pour remplacer ceci:

Events.handler(Handshake::class, object : EventHandler<Handshake> {
    override fun handle(event: Handshake) {
        println(event.sent)
    }
})

Avec un plus pratique:

Events.handler(Handshake::class, EventHandler<Handshake> { println(it.sent) })

Pour une raison quelconque en référence à EventHandler:

enter image description here

Plus préférablement cependant, j'aimerais utiliser quelque chose d'encore plus court, comme ceci: Events.handler(Handshake::class, { println(it.sent) })

Ou utilisez la fonctionnalité publiée pour utiliser la méthode comme ceci: Events.handler(Handshake::class) { println(it.sent) }

Voici mon objet Events:

import Java.util.*
import kotlin.reflect.KClass

object Events {

    private val map = HashMap<Class<*>, Set<EventHandler<*>>>()

    fun <T : Any> handler(eventType: KClass<T>, handler: EventHandler<T>) {
        handler(eventType.Java, handler)
    }

    fun <T> handler(eventType: Class<T>, handler: EventHandler<T>) = handlers(eventType).add(handler)

    fun post(event: Any) = handlers(event.javaClass).forEach { it.handle(event) }

    operator fun plus(event: Any) = post(event)

    private fun <T> handlers(eventType: Class<T>): HashSet<EventHandler<T>> {
        var set = map[eventType]
        if (set == null) {
            set = HashSet<EventHandler<*>>()
            map.put(eventType, set)
        }
        return set as HashSet<EventHandler<T>>
    }

}

Et mon interface EventHandler:

@FunctionalInterface
interface EventHandler<T> {

    fun handle(event: T)

}
42
Jire

En supposant que plus bas, vous avez vraiment besoin de EventHandler comme interface distincte (par exemple pour Java interop). Sinon, vous pouvez simplement utiliser un alias de type (depuis Kotlin 1.1) :

typealias EventHandler<T> = (T) -> Unit

Dans ce cas, un simple lambda fonctionnera immédiatement.

Mais si vous ne voulez pas utiliser un alias de type, le problème persiste. C’est que Kotlin ne fait la conversion SAM que pour les fonctions définies en Java. Puisque Events.handler est défini dans Kotlin, les conversions SAM ne lui sont pas applicables.

Pour supporter cette syntaxe:

Events.handler(Handshake::class, EventHandler<Handshake> { println(it.sent) })

Vous pouvez définir une fonction nommée EventHandler:

fun <T> EventHandler(handler: (T) -> Unit): EventHandler<T> 
    = object : EventHandler<T> { 
        override fun handle(event: T) = handler(event) 
    }

Pour supporter cette syntaxe:

Events.handler(Handshake::class, { println(it.sent) })

ou ca:

Events.handler(Handshake::class) { println(it.sent) }

Vous devez surcharger la fonction handler pour prendre une fonction à la place de EventHandler:

fun <T> Events.handler(eventType: Class<T>, handler: (T) -> Unit) = EventHandler(handler)

NOTE: Quand une future version de Kotlin aura des alias de type, l'interface EventHandler ne sera plus nécessaire, car elle sera remplacée par un alias nommé du type (T) -> Unit

67
Andrey Breslav