2016-12-12 36 views
5

我有一個使用泛型下面的代碼:科特林仿製藥:在通用映射參數類型不匹配

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) 

它給出了一個錯誤:

Type mismatch. Expected ValueConverter<Event>. Found 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() 
    } 

} 

但問題是像調用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的類?

回答

8

該錯誤是在這條線:

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

不過你已經知道了泛型類型,因爲你的地圖關鍵是泛型類型參數的名字!

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

如果你是好奇,爲什麼編譯器並沒有抱怨你的轉換器功能早些時候:記住怎樣地圖只能容納ValueConverter<Event>

所以,簡單地通過地圖與力轉換返回的值只有那個班?這意味着編譯器知道你可以將Event的任何子類傳遞給這個轉換器。一旦你改變成一個明星投影類型,編譯器不知道這可能是一個ValueConverter<FooEvent>ValueConverter<BazEvent>等 - 使一個給定的轉換器的有效函數簽名在地圖convert(event: Nothing)

因爲沒有是一個有效的輸入。

+0

謝謝!有用。 –