如何防止 Kotlin 中的类型擦除?

Dmi*_*gin 3 generics type-erasure kotlin

您能否建议如何使其工作Kotlin

interface SomeHandler 
interface Handler<T> : SomeHandler {
    fun handle(e: T)
}

class Sender(vararg val handler: SomeHandler) {
    fun <T> send(e: T) {
        for (h in handler)
            if (h is Handler<T>)
                h.handle(e) // ERROR: Cannot check for instance 
                            // of erased type: Scratch.Handler<T>
    }
}
Run Code Online (Sandbox Code Playgroud)

我只想在这里调用所有匹配的处理程序。

gid*_*dds 5

恐怕你不能那样做。\xc2\xa0 在运行时,只有\Handler<T> xe2\x80\x94 Handler。\xc2\xa0 (类型已被删除。)\xc2 \ Txa0 所以进行此类检查的唯一方法是确定T其他方法。

\n

例如,您可以将类型存储为额外字段Handler,例如:

\n
interface Handler<T : Any> : SomeHandler {\n    val type: KClass<T>\n    fun handle(e: T)\n}\n\nclass Sender(vararg val handler: SomeHandler) {\n    fun <T : Any> send(e: T) {\n        for (h in handler)\n            if (h is Handler<*> && h.type.isInstance(e))\n                (h as Handler<T>).handle(e)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这里Handler有一个额外的type字段,需要由实现该类型的每个类的构造函数初始化。\xc2\xa0 类型参数需要一个界限Any(即必须是不可空的,以匹配KClass)。\xc2\xa0并且as给出警告作为未经检查的强制转换 \xe2\x80\x94 但由于检查isInstance()(以及该type值与类型参数绑定的方式T),它应该是安全的。

\n

另一种方法可能是将处理程序存储在 a 中,Map其中处理程序的类型作为键,相应处理程序的列表作为值,例如:

\n
interface Handler<T> : SomeHandler {\n    fun handle(e: T)\n}\n\nclass Sender(val handlers: Map<KClass<*>, List<SomeHandler>>) {\n    fun <T> send(e: T) {\n        handlers[T::class]?.forEach {\n            (it as Handler<T>).handle(e)\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这种方法的优点是它的效率可能会稍微高一些,因为它可以直接进入相关的处理程序,而无需迭代任何其他处理程序,并且不执行任何运行时类型检查。\xc2\xa0\xc2\xa0但是,它是不太安全:它依赖于您设置映射handlers,以便每个列表仅Handler包含其映射键中给定类型的 s。\xc2\xa0 类型系统无法为您强制执行此操作:再一次,这as是未经检查的强制转换,如果地图设置不正确,您可能会遇到运行时异常。

\n

我不能推荐这两种方法中的任何一种:两种方法都很丑陋,都有一些开销,而且后者不是很安全。\xc2\xa0(当然也可能有其他方法,但我怀疑他们会有类似的方法)问题。)

\n

也许值得看看你的大局,看看你是否需要这样做;您也许能够进行架构更改,从而提供更简洁、更安全的方法。\xc2\xa0(例如,您可以Sender根据其使用的类型来研究参数化Handler。)

\n