Java/Kotlin 为具有通用 return 类型的访问者模式转换异常
Java/Kotlin cast exception for visitor pattern with generic return type
我正在尝试使用类似访客模式的东西, 和 return 值。
然而,虽然没有显式转换,但我收到了 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))
}
我不知道是什么问题。我是在尝试做一些不可能的事情,还是我没有正确表达它,或者是类型擦除使一些应该可能的事情变得不可能?
到目前为止我的想法:
Printer.combine
预计 CharSequence
秒;
- 我正在调用
TreeElem.accept
的通用重载 returns CharSequence
- 编译器可能会在 JVM 代码中插入强制转换(类型擦除?)
- 但是运行时类型是兼容的,所以转换应该可以工作
由于最后一点与现实主义相冲突,我可能理解有误。
编辑:我已将 MWE 翻译成 Java 以查看它是否是 Kotlin 问题并吸引答案:
interface TreeElem {
<T> T accept(TreeVisitor<T> visitor);
}
class Leaf implements TreeElem {
public <T> T accept(TreeVisitor<T> visitor) {
return visitor.visit(this);
}
}
class Split implements TreeElem {
private TreeElem left;
private TreeElem right;
Split(TreeElem left, TreeElem right) {
this.left = left;
this.right = right;
}
public <T> T accept(TreeVisitor<T> visitor) {
return visitor.combine(
visitor.visit(this),
left.accept(visitor),
right.accept(visitor));
}
}
interface TreeVisitor<T> {
T visit(Leaf tree);
T visit(Split tree);
T combine(T... inputs);
}
class Printer implements TreeVisitor<CharSequence> {
public CharSequence combine(CharSequence... inputs) {
StringBuilder text = new StringBuilder();
for (CharSequence input : inputs) {
text.append(input);
}
return text;
}
public CharSequence visit(Leaf tree) { return "leaf"; }
public CharSequence visit(Split tree) { return "split"; }
}
public class MWEjava {
public static void main(String[] args) {
TreeElem tree = new Split(new Leaf(), new Leaf());
Printer printer = new Printer();
System.out.println(tree.accept(printer));
}
}
错误与 Java 情况相同。
我很确定这是这个问题的重复:
但是,要提供特定的解决方案,您可以将 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(" ")
}
// ...
}
不是很漂亮,但它与泛型配合得很好。
我正在尝试使用类似访客模式的东西,
然而,虽然没有显式转换,但我收到了 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))
}
我不知道是什么问题。我是在尝试做一些不可能的事情,还是我没有正确表达它,或者是类型擦除使一些应该可能的事情变得不可能?
到目前为止我的想法:
Printer.combine
预计CharSequence
秒;- 我正在调用
TreeElem.accept
的通用重载 returnsCharSequence
- 编译器可能会在 JVM 代码中插入强制转换(类型擦除?)
- 但是运行时类型是兼容的,所以转换应该可以工作
由于最后一点与现实主义相冲突,我可能理解有误。
编辑:我已将 MWE 翻译成 Java 以查看它是否是 Kotlin 问题并吸引答案:
interface TreeElem {
<T> T accept(TreeVisitor<T> visitor);
}
class Leaf implements TreeElem {
public <T> T accept(TreeVisitor<T> visitor) {
return visitor.visit(this);
}
}
class Split implements TreeElem {
private TreeElem left;
private TreeElem right;
Split(TreeElem left, TreeElem right) {
this.left = left;
this.right = right;
}
public <T> T accept(TreeVisitor<T> visitor) {
return visitor.combine(
visitor.visit(this),
left.accept(visitor),
right.accept(visitor));
}
}
interface TreeVisitor<T> {
T visit(Leaf tree);
T visit(Split tree);
T combine(T... inputs);
}
class Printer implements TreeVisitor<CharSequence> {
public CharSequence combine(CharSequence... inputs) {
StringBuilder text = new StringBuilder();
for (CharSequence input : inputs) {
text.append(input);
}
return text;
}
public CharSequence visit(Leaf tree) { return "leaf"; }
public CharSequence visit(Split tree) { return "split"; }
}
public class MWEjava {
public static void main(String[] args) {
TreeElem tree = new Split(new Leaf(), new Leaf());
Printer printer = new Printer();
System.out.println(tree.accept(printer));
}
}
错误与 Java 情况相同。
我很确定这是这个问题的重复:
但是,要提供特定的解决方案,您可以将 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(" ")
}
// ...
}
不是很漂亮,但它与泛型配合得很好。