I recently tried the following in Kotlin. The idea is that I will receive as input an Item (like AmericanItem for instance) that extends BaseItem. I am trying to have a different parser for each of theses items Here is a sample code

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

我的问题是在最后一行,当我试图调用解析器时,我得到了以下编译错误

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

What am I doing wrong here ?

推荐答案

You have created a conflict between your declarations of the Map and of the ItemParser. The map can contain any descendant of BaseItem but the ItemParser is designed that each descendant only operates on one of the descendants of BaseItem. So for a given instance of ItemParser it must accept something it can recognize and here you can't do that because your foundParser could be any descendant and not the one true expected type for that given ItemParser instance. Which T should it guess at?!? It cannot.

Therefore you have to design your API around the base class and not the descendants. You make it impossible for the compiler to know what is be passed to the parse() method. The only one true thing you can know is that it is a BaseItem instance.

只有you人知道你在使用map做什么,保证你用正确的类型调用正确的实例.编译器不知道你的逻辑,这就是保证.

I would suggest you change your API to add an internalParse method for which you cast do your work, wrapped by a generic parse function that double checks and does the evil cast.

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

Note you could also use the Kotlin class instead of the Java class which would be of type KClass<out T>.

Kotlin相关问答推荐

Kotlin—列出具有不同T的列表之间的操作'

我如何测试一个可组合组件没有显示,但如果它不存在也接受?

Kotlin中一个接口的实现问题

Kotlin 中的密封和内部有什么区别?

JetPack Compose:添加点击持续时间

如何将glide显示的图像下载到设备存储中.Kotlin

如何为你的 Flutter 元素添加 Kotlin 支持?

如何在 android jetpack compose 中相互重叠列表项?

API 'variant.getJavaCompile()' 已过时

Kotlin 创建snackbar

如何使用Kotlin Dokka记录主构造函数参数

Jetpack Compose – LazyColumn 不重组

从 Spring WebFlux 返回 Flux 返回一个字符串而不是 JSON 中的字符串数组

未找到导入 kotlinx.coroutines.flow.*

Android 与 Kotlin - 如何使用 HttpUrlConnection

ObjectAnimator.ofFloat 不能直接在kotlin中取Int作为参数

Android Kotlin 创建类实现 Parcelable 在 writeToParcel 方法的 override中给出错误

Kotlin中的Memoization功能

Kotlin - 是否可以在类中的 init 块之前初始化伴随对象?

Java的Kotlin:字段是否可以为空?