Generics Kotlin out投影类型禁止使用
我最近在Kotlin尝试了以下方法。 我的想法是,我将接收一个扩展了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
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)
}