2016-12-12 4 views
5

で型の不一致私はジェネリックを使用して、次のコードがあります。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) 

それがエラーを与える:

Type mismatch. Expected ValueConverter<Event>. Found ValueConverter<E>.

私もこのようなものを試しました:

以下のようなもので 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を実装しているクラスを使用できるようにジェネリックスを宣言し、イベントを拡張するクラスを受け入れるにはどうすればよいですか?

答えて

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のサブクラスをこのコンバーターに渡すことができることを知っていました。

何もないので:convert(event: Nothing)あなたのマップで与えられたコンバータの実効関数のシグネチャを作る - あなたはスター投影型に変更したら、これはなどValueConverter<FooEvent>、またはValueConverter<BazEvent>、あるかもしれない場合、コンパイラは知りません有効な入力です。

+0

ありがとうございます!できます。 –

関連する問題