Kotlin的泛型:通用映射参数中的类型不匹配

Pab*_*oso 5 generics kotlin

我有以下使用泛型的代码:

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()
    }

}
Run Code Online (Sandbox Code Playgroud)

但在这条线上:

converters.put(converter.getEventClass().name, converter)
Run Code Online (Sandbox Code Playgroud)

它给出了一个错误:

类型不匹配.期望ValueConverter <Event>.找到ValueConverter <E>.

我也尝试过这样的事情:

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()
    }

}
Run Code Online (Sandbox Code Playgroud)

但问题是当用以下方式调用ValueConverters.register()时:

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())
Run Code Online (Sandbox Code Playgroud)

它还给出了类似的类型不匹配错误.

我应该如何声明泛型,以便我可以使用任何实现ValueConverter的类并接受任何扩展Event的类?

F. *_*rge 10

错误在这一行:

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

此地图的值仅限于ValueConverter<Event>.所以,如果你有一个班级

class FooEvent : Event

和值转换器:

ValueConverter<FooEvent>,

您无法在地图中存储该值转换器.你真正想要的是一种*星形投影类型.

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

现在你可以在地图中放置任何值转换器.


然而,这揭示了另一个问题:怎么做

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

知道地图中返回的转换器的泛型类型是什么?毕竟,地图可能包含多个不同事件类型的转换器!

IntelliJ迅速抱怨:

Out-projection类型'ValueConverter <*>?'  禁止使用'public abstract fun convert(event:E):在ValueConverter中定义的Float'.

但是您已经知道泛型类型,因为您的map键是泛型类型参数的名称!

因此,只需使用force强制转换返回的值:

@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)
}
Run Code Online (Sandbox Code Playgroud)

如果你很好奇为什么编译器没有早先抱怨你的转换器功能:还记得你的地图只能容纳ValueConverter<Event>那个类吗?这意味着编译器知道您可以将任何子类传递Event到此转换器中.一旦你改为星型投影类型,编译器就不知道这可能是a ValueConverter<FooEvent>还是a ValueConverter<BazEvent>等. - 在地图中制作给定转换器的有效函数签名convert(event: Nothing):

因为没有什么是有效的输入.