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

时间:2016-12-12 13:40:59

标签: 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()
    }

}

但是就这一行:

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

它出错了:

  

类型不匹配。预期ValueConverter&lt;事件&gt;。找到ValueConverter&lt; E&gt;。

我也试过这样的事情:

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

}

但问题是在调用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())

它也会出现类似的类型不匹配错误。

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

1 个答案:

答案 0 :(得分:9)

错误在这一行:

ValueConverter<Event>

此地图的值仅限于class FooEvent : Event。所以,如果你有一个班级

ValueConverter<FooEvent>

和值转换器:

*

您无法在地图中存储该值转换器。你真正想要的是一个private val converters = HashMap<String, ValueConverter<*>>()星投影类型。

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

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

然而,这揭示了另一个问题:如何

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

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

IntelliJ迅速抱怨:

Out-projected type 'ValueConverter<*>?' prohibits the use of 'public abstract fun convert(event: E): Float defined in ValueConverter'.

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

所以简单地用force强制转换返回的值:

ValueConverter<Event>

如果您很好奇为什么编译器之前没有抱怨您的转换器功能:请记住您的地图只能保留Event而只能保留该类?这意味着编译器知道您可以将ValueConverter<FooEvent>的任何子类传递到此转换器中。一旦您更改为星型投影类型,编译器就不知道这可能是ValueConverter<BazEvent>还是convert(event: Nothing)等。 - 在您的指针中创建给定转换器的有效函数签名地图EXIFactory.getFidelityOptions().setFidelity(FidelityOptions.FEATURE_PREFIX, true);

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