Generics Kotlin out投影类型禁止使用

Generics Kotlin out投影类型禁止使用,generics,kotlin,Generics,Kotlin,我最近在Kotlin尝试了以下方法。 我的想法是,我将接收一个扩展了BaseItem的项作为输入(例如AmericanItem)。 我正在尝试为这些项目中的每一项使用不同的解析器 下面是一个示例代码 abstract class BaseItem class AmericanItem : BaseItem() class EuropeanItem : BaseItem() interface ItemParser<T : BaseItem> { fun parse(item

我最近在Kotlin尝试了以下方法。 我的想法是,我将接收一个扩展了
BaseItem
项作为输入(例如
AmericanItem
)。 我正在尝试为这些项目中的每一项使用不同的解析器 下面是一个示例代码

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)
}
抽象类BaseItem
类AmericanItem:BaseItem()
类EuropeanItem:BaseItem()
接口项分析器{
趣味解析(项目:T)
}
类AmericanItemParser:ItemParser{
覆盖趣味解析(项目:AmericanItem){
println(“AmericanTemparser”)
}
}
类EuropeanItemParser:ItemParser{
重写趣味解析(项目:EuropeanItem){
println(“解析EuropeanItem”)
}
}
趣味主线(args:Array){
val hashMap=hashMap()
put(AmericanItem::class.java,EuropeanItemParser())
put(EuropeanItem::class.java,AmericanItemParser())
val inputItem=EuropeanItem()
val foundParser=hashMap[inputItem.javaClass]
foundParser?.parse(输入项)
}
我的问题是在最后一行,当我试图调用解析器时,我得到了以下编译错误

Out-projected type 'ItemParser<*>?' prohibits the use of 'public abstract fun parse(item: T): kotlin.Unit defined in ItemParser'
Out投影类型'ItemParser'禁止使用'public abstract fun parse(item:T):kotlin.Unit在ItemParser中定义'
我做错了什么

ItemParser
是未知
T
而不是任何
T
的解析器。由于
T
是未知的,因此没有任何值可以安全地传递给需要
T
的函数
parse

如果确定映射包含给定
键的
ItemParser
,则可以编写以下使用未选中强制转换的帮助函数:

fun <T : BaseItem> Map<Class<out T>, ItemParser<*>>.findParserFor(item: T) =
    get(item.javaClass) as ItemParser<T>?
注意,在您的示例中,映射将
AmericanItem::class.java
键关联到
EuropeanItemParser
,反之亦然

hashMap.put(AmericanItem::class.java, EuropeanItemParser())
hashMap.put(EuropeanItem::class.java, AmericanItemParser())
由于运行时出现异常,代码将失败:

ClassCastException:EuropeanItem不能强制转换为AmericanItem


当您将
EuropeanItem
传递到
parse()
函数时,而不是在
findParserFor
函数中,将发生异常。这是因为返回的
ItemParser
实例实际上是一个
AmericanItemParser
。这是使用未检查强制转换的结果,假设类型匹配,而实际上它们不匹配,这就是为什么该强制转换被称为“未检查”。

您在
映射的声明和
项解析器的声明之间创建了冲突。映射可以包含
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)
}
抽象类BaseItem
类AmericanItem:BaseItem()
类EuropeanItem:BaseItem()
接口项分析器{
@抑制(“未选中的_CAST”)
趣味解析(项目:BaseItem){
val tempItem=项目为?T
?:抛出IllegalArgumentException(“传递到此解析器的无效类型${item.javaClass.name}”)
内部解析(tempItem)
}
内部分析(项目:T)
}
类AmericanItemParser:ItemParser{
重写内部解析(项目:AmericanItem){
println(“AmericanTemparser”)
}
}
类EuropeanItemParser:ItemParser{
重写内部解析(项目:EuropeanItem){
println(“解析EuropeanItem”)
}
}
趣味主线(args:Array){
val hashMap=hashMap()
put(AmericanItem::class.java,EuropeanItemParser())
put(EuropeanItem::class.java,AmericanItemParser())
val inputItem=EuropeanItem()
val foundParser=hashMap[inputItem.javaClass]
foundParser?.parse(输入项)
}
注意,您还可以使用Kotlin类而不是Java类,Java类的类型为
KClass

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