2017-12-30 30 views
1

訪問者パターンbutのようなものを戻り値とともに使用しようとしています。汎用戻り型の訪問者パターンのJava/Kotlinキャスト例外

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(...) 

コード:明示的なキャストが存在しないものの

しかし、私はClassCastExceptionが取得しています

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.combineCharSequenceの期待します。
  2. 私はCharSequence
  3. コンパイラはおそらくJVMコード(型消去?)
  4. にキャストを挿入しかし、ランタイム型に互換性があるので、キャストは
  5. を動作するはずが返さ TreeElem.acceptの一般的な過負荷を呼んでいます

最後の点は現実主義者と衝突しているので、間違って何かを理解している可能性があります。

編集:私はそれがKotlinの問題だかどうかを確認するために、答えを誘致するためにJavaにMWEを翻訳しました:

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の場合も同じです。

答えて

1

私は、これは、この質問の重複しているかなり確信している:

https://stackoverflow.com/a/9058259/4465208

しかし、具体的なソリューションを提供するために、あなただけ正常に動作します代わりにList<T>vararg引数を置き換えることができます

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(" ") 
    } 
    // ... 
} 

綺麗ではありませんが、ジェネリックでうまくいきます。

+1

これは意味があります、ありがとう!どういうわけか、私は '' L 'を認識しませんでした。それは配列なのでキャストが失敗したかもしれません... – Mark

+0

それは逃すのは簡単ですが、私にはいつも起こります:) – zsmb13

関連する問題