Исключение Java / Kotlin для шаблона посетителя с типичным типом возврата

Я пытаюсь использовать что-то вроде шаблона посетителя, но с возвращаемыми значениями.

Однако, хотя явных приведений нет, я получаю 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 общую перегрузку TreeElem.accept которая возвращает CharSequence
  3. Возможно, компилятор вставляет листинг в код JVM (тип erasure?)
  4. Но типы выполнения совместимы, поэтому приведение должно работать

Поскольку последний момент находится в конфликте с реалистом, я, вероятно, понимаю что-то неправильно.

EDIT: я перевел MWE на Java, чтобы узнать, является ли это проблемой Котлина и привлекает ответ:

 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.

Я уверен, что это дубликат этого вопроса: 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(" ") } // ... } 

Не так красиво, но хорошо играет с дженериками.

Intereting Posts
База данных Firebase сериализует int для перечисления? Подклассификация класса для изменения переменной параметра типа в Котлине Использование в операторе для сравнения строки с диапазоном строк Mockito с тестированием асинхронного тестирования Kotlin Виджет PlaceAutocomplete не работает в Android 4.1 Как установить CallBack в Котлин? Kotlin для игры dev POJO-объект для этого XML-ответа в котлине Я хочу определить, является ли класс JVM классом Котлина или нет Возможно ли реализовать индивидуальное деструктурирование для не-класса данных в Котлин? Как использовать выражение `with` от Kotlin для типов с нулевым значением Почему вы не можете указывать петли var / val в Котлине? Анимация (прокрутка) одна ViewGroup блокирует несвязанные виды из анимации до завершения В Kotlin, как мне добавить методы расширения в другой класс, но только видимый в определенном контексте? Отражение Java-конструктора исключает исключение с помощью закрытых классов