Java / Kotlin为通用返回类型的访问者模式强制转换异常

时间:2017-12-30 10:41:17

标签: java generics kotlin type-erasure visitor

我尝试使用访问者模式but等返回值。

但是,虽然没有明确的演员表,但我得到了ClassCastException:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.CharSequence;
    at Printer.combine(...)
    at Split.accept(...)
    at MWEKt.main(...)

代码:

interface TreeElem {
    fun <T> accept(visitor: TreeVisitor<T>): T
}

class Leaf: TreeElem {
    override fun <T> accept(visitor: TreeVisitor<T>): T {
        return visitor.visit(this)
    }
}

class Split(val left: TreeElem, val right: TreeElem): TreeElem {
    override fun <T> accept(visitor: TreeVisitor<T>): T {
        return visitor.combine(  // this causes cast error
            visitor.visit(this),
            left.accept(visitor),
            right.accept(visitor))
    }
}

interface TreeVisitor<T> {
    // multiple implementations with different T in future (only one in this example)
    fun visit(tree: Leaf): T
    fun visit(tree: Split): T
    fun combine(vararg inputs: T): T
}

class Printer: TreeVisitor<CharSequence> {
    override fun combine(vararg inputs: CharSequence): CharSequence { // error here
        return inputs.joinToString(" ")
    }
    override fun visit(tree: Leaf): CharSequence { return "leaf" }
    override fun visit(tree: Split): CharSequence { return "split" }
}

fun main(args: Array<String>) {
    val tree = Split(Leaf(), Leaf())
    val printer = Printer()
    println(tree.accept(printer))
}

我不知道问题所在。我是在尝试做一些不可能做到的事情,还是我没有正确地表达出来,或者是类型擦除使某些事情变得不可能?

到目前为止我的想法:

  1. Printer.combine期待CharSequence s;
  2. 我正在调用返回TreeElem.accept的{​​{1}}的泛型重载
  3. 编译器可能会在JVM代码中插入一个强制转换(类型擦除?)
  4. 但运行时类型是兼容的,因此强制转换应该正常工作
  5. 由于最后一点与现实主义者发生冲突,我可能不正确地理解错误。

    编辑:我已将MWE翻译成Java,看看它是否是Kotlin问题并吸引答案:

    CharSequence

    Java案例的错误相同。

1 个答案:

答案 0 :(得分:1)

我很确定这是这个问题的重复:https://stackoverflow.com/a/9058259/4465208

但是,要提供特定的解决方案,您可以将vararg参数替换为List<T>代替,这样就可以了:

class Split(val left: TreeElem, val right: TreeElem) : TreeElem {
    override fun <T> accept(visitor: TreeVisitor<T>): T {
        return visitor.combine(listOf(
                visitor.visit(this),
                left.accept(visitor),
                right.accept(visitor)))
    }
}

interface TreeVisitor<T> {
    fun combine(inputs: List<T>): T
    // ...
}

class Printer : TreeVisitor<CharSequence> {
    override fun combine(inputs: List<CharSequence>): CharSequence {
        return inputs.joinToString(" ")
    }
    // ...
}

不是那么漂亮,但它与泛型一致。