Kotlin突出型禁止使用

Dam*_*que 7 generics kotlin

我最近在科特林尝试了以下方法。我的想法是,我将收到一个ItemAmericanItem例如)extends 作为输入BaseItem。我试图为这些项目中的每个项目使用不同的解析器,这是示例代码

abstract class BaseItem
class AmericanItem : BaseItem()
class EuropeanItem : BaseItem()

interface ItemParser<T : BaseItem> {
    fun parse(item: T)
}

class AmericanItemParser : ItemParser<AmericanItem> {
    override fun parse(item: AmericanItem) {
        println("AmericanItemParser")
    }
}

class EuropeanItemParser : ItemParser<EuropeanItem> {
    override fun parse(item: EuropeanItem) {
        println("parsing EuropeanItem")
    }
}

fun main(args: Array<String>) {
    val hashMap = HashMap<Class<out BaseItem>, ItemParser<*>>()
    hashMap.put(AmericanItem::class.java, EuropeanItemParser())
    hashMap.put(EuropeanItem::class.java, AmericanItemParser())

    val inputItem = EuropeanItem()
    val foundParser = hashMap[inputItem.javaClass]
    foundParser?.parse(inputItem)
}
Run Code Online (Sandbox Code Playgroud)

我的问题在最后一行,当我尝试调用解析器时,出现以下编译错误

Out-projected type 'ItemParser<*>?' prohibits the use of 'public abstract fun parse(item: T): kotlin.Unit defined in ItemParser'
Run Code Online (Sandbox Code Playgroud)

我在这里做错了什么?

Jay*_*ard 5

您在Map和的声明之间创建了冲突ItemParser。该地图可以包含的任何后代BaseItem,但ItemParser设计成每个后代只能对一个的后裔BaseItem。因此,对于给定的实例,ItemParser它必须接受它可以识别的某些内容,在这里您不能这样做,因为您foundParser可以是任何后代,而不是该给定ItemParser实例的一个真正的预期类型。它T应该猜哪个?!这不可以。

因此,您必须围绕基类而不是后代设计API。您使编译器无法知道将传递给该parse()方法的内容。您可以知道的唯一一件事是它是一个BaseItem实例。

只有知道使用映射进行操作的技巧,可以确保您使用正确的类型调用正确的实例。编译器不知道使您保证的逻辑。

我建议您更改您的API,以添加一个internalParse您要强制执行其工作的方法,并由一个经过仔细parse检查和执行恶意强制转换的通用函数来包装。

abstract class BaseItem

class AmericanItem : BaseItem()
class EuropeanItem : BaseItem()

interface ItemParser<T: BaseItem> {
    @Suppress("UNCHECKED_CAST")
    fun parse(item: BaseItem) {
        val tempItem = item as? T 
             ?: throw IllegalArgumentException("Invalid type ${item.javaClass.name} passed to this parser")
        internalParse(tempItem)
    }

    fun internalParse(item: T)
}

class AmericanItemParser : ItemParser<AmericanItem> {
    override fun internalParse(item: AmericanItem) {
        println("AmericanItemParser")
    }
}

class EuropeanItemParser : ItemParser<EuropeanItem> {
    override fun internalParse(item: EuropeanItem) {
        println("parsing EuropeanItem")
    }
}

fun main(args: Array<String>) {
    val hashMap = HashMap<Class<out BaseItem>, ItemParser<*>>()
    hashMap.put(AmericanItem::class.java, EuropeanItemParser())
    hashMap.put(EuropeanItem::class.java, AmericanItemParser())

    val inputItem = EuropeanItem()
    val foundParser = hashMap[inputItem.javaClass]
    foundParser?.parse(inputItem)
}
Run Code Online (Sandbox Code Playgroud)

请注意,您也可以使用Kotlin类而不是类型为Java的Java类KClass<out T>