Genéricos de Kotlin: tipo de desajuste en el parámetro de map genérico

Tengo el siguiente código que usa generics:

abstract class Event(val name: String) interface ValueConverter<E : Event> { fun convert(event: E): Float fun getEventClass(): Class<E> } class ValueConverters { private val converters = HashMap<String, ValueConverter<Event>>() fun <E : Event> register(converter: ValueConverter<E>) { converters.put(converter.getEventClass().name, converter) } fun unregister(eventClass: Class<Event>) { converters.remove(eventClass.name) } fun <E : Event> convert(event: E): Float { return converters[event.javaClass.name]?.convert(event) ?: 0.0f } fun clear() { converters.clear() } } 

Pero en esta línea:

 converters.put(converter.getEventClass().name, converter) 

da un error:

Escriba desajuste. ValueConverter <Evento> esperado. Found ValueConverter <E>.

También probé algo como esto:

 class ValueConverters { private val converters = HashMap<String, ValueConverter<Event>>() fun register(converter: ValueConverter<Event>) { converters.put(converter.getEventClass().name, converter) } fun unregister(eventClass: Class<Event>) { converters.remove(eventClass.name) } fun convert(event: Event): Float { return converters[event.javaClass.name]?.convert(event) ?: 0.0f } fun clear() { converters.clear() } } 

Pero el problema es cuando se llama a ValueConverters.register() con algo como:

 class SampleEvent1 : Event(name = SampleEvent1::class.java.name) class SampleValueConverter1 : ValueConverter<SampleEvent1> { override fun convert(event: SampleEvent1): Float = 0.2f override fun getEventClass(): Class<SampleEvent1> = SampleEvent1::class.java } converters.register(converter = SampleValueConverter1()) 

También da el error de discrepancia de tipo similar.

¿Cómo debo declarar los generics para poder usar cualquier class que implemente ValueConverter y acepte cualquier class que amplíe Event?

El error está en esta línea:

private val converters = HashMap<String, ValueConverter<Event>>()

Los valores de este map están limitados a ValueConverter<Event> . Entonces, si tienes una class

class FooEvent : Event

y un convertidor de valor:

ValueConverter<FooEvent> ,

no puedes almacenar ese convertidor de valor en tu map. Lo que realmente querría es un tipo de proyección * estrella.

private val converters = HashMap<String, ValueConverter<*>>()

Ahora puedes poner cualquier convertidor de valor en el map.


Sin embargo, esto revela otro problema: ¿Cómo

fun <E : Event> convert(event: E): Float

saber cuál es el tipo genérico del convertidor devuelto en el map? ¡Después de todo, el map puede contener múltiples convertidores para diferentes types de events!

IntelliJ rápidamente se queja:

¿Out-proyectado tipo 'ValueConverter <*>?' prohíbe el uso de 'public fun free convert (event: E): Float definido en ValueConverter'.

Pero ya sabes el tipo genérico porque tu key de map es el nombre del parámetro de tipo genérico.

Así que simplemente echa el valor devuelto por el map con fuerza:

 @Suppress("UNCHECKED_CAST") fun <E : Event> convert(event: E): Float { val converter = converters[event.javaClass.name] ?: return 0.0f return (converter as ValueConverter<E>).convert(event) } 

Si tiene curiosidad, ¿por qué el comstackdor no se quejó antes de su function de conversión? ¿Restring que su map solo podía contener ValueConverter<Event> y solo esa class? Esto significa que el comstackdor sabía que podía pasar cualquier subclass de Event a este convertidor. Una vez que haya cambiado a un tipo de proyección en estrella, el comstackdor no sabe si esto podría ser un ValueConverter<FooEvent> , o un ValueConverter<BazEvent> , etc. – haciendo que la function efectiva sea la firma de un convertidor dado en su convert(event: Nothing) map convert(event: Nothing) :

porque nada es una input válida.

  • Room - SELECT query, get o default
  • Error de desencryption de Phpseclib al descifrar el contenido encriptado en Java
  • ¿A los miembros protegidos no se puede acceder en las funciones de extensión?
  • Kotlin - Jackson ignora los valores nulos
  • Reflexión de Kotlin: parámetro de tipo desconocido
  • Cómo mezclar múltiples constructores de class padre con val en class infantil
  • Kotlin 1.1.3-2 No se puede crear una instancia de fragment en OnResume Android
  • ¿Cómo puedo indicar explícitamente la finalización de un Flowable en RxJava?
  • Renderiza la respuesta json en Kotlin
  • Android create listitem / object con información guardada de otra actividad
  • time de espera de connection: conectar consultar logging de IDE