Нечетная ошибка ввода в Scala
Взгляните на это:
scala> class Container(val rows: Iterable[Iterable[Option[Any]]]) {}
defined class Container
scala> val row1 = Array(Some("test"),Some(1234))
row1: Array[Some[Any]] = Array(Some(test), Some(1234))
scala> val row2 = Array(Some("test2"), Some(12345))
row2: Array[Some[Any]] = Array(Some(test2), Some(12345))
scala> val listtest = List(row1, row2)
listtest: List[Array[Some[Any]]] = List(Array(Some(test), Some(1234)), Array(Some(test2), Some(12345)))
scala> val test = new Container(listtest)
<console>:11: error: type mismatch;
found : List[Array[Some[Any]]]
required: Iterable[Iterable[Option[Any]]]
val test = new Container(listtest)
^
scala> val test = new Container(List(row1,row2))
test: Container= [email protected]
Как получилось определение контейнера, второй способ работает, а первый - нет? Разве не те же самые типы?
Ответы
Ответ 1
Это не ошибка. Array
не является ковариантным. B, являющийся подтипом B, не делает Array [B] подтипом Array [A]. Это противоречит java, где B [] является подтипом A [], который является необоснованным:
A[] b = new B[1];
b[0] = new (A);
-> ArrayStoreException
Итак, ваш массив [Some [Any]] не является массивом [Option [Any]]. Вы должны убедиться, что у вас есть Array [Option], который вы можете сделать с помощью
val row2 = Array[Option[Any]](Some(test2), Some(12345))
Вы также можете использовать абзац типа в одном из элементов:
val row2 = Array(Some(test2): Option[String], Some(12345))
Или, если вы знаете, что ваши значения не равны нулю,
val row2 = Array(Option(test2), Option(12345))
(достаточно сделать это на одном из значений)
List
, с другой стороны, является ковариантным, поэтому он и работает.
На самом деле, к сожалению, более точный тип Some
выведен, весьма необычно, что вы хотите, чтобы тип чего-то назывался Some
(или None
), а не Option
.
Изменить Извините, похоже, я полностью пропустил суть, о том, почему есть разные. Array
не ковариант, но Iterable. Таким образом, кажется, что Array[B]
, не будучи Array[A]
, должен быть Iterable[A]
, тогда все должно работать. Это было бы так, если бы Array был подтипом Iterable. Это не означает, что он поставляется с JVM, и не может быть расширена Iterable
. Имеется неявное преобразование в WrappedArray
, которое является Iterable
.
Когда вы пишете val l = List(row1, row2)
, у него нет причин применять это преобразование. Он печатает список как можно точнее. Тогда тот факт, что List является ковариантным (список [B] является списком [A], если B является A) не будет пинать, когда мы не являемся B, является A, но B имеет неявное преобразование в A.
С другой стороны, когда вы пишете val l: List [Iterable [A]] = List (x, y), тогда функция List (...) ожидает Iterable [A] аргументов, и на данный момент она выглядит для неявных преобразований.
Все еще не ошибка, но сложнее, чем я думал. Возможно, вы могли бы сделать
class Container[T <% Iterable[Option[Any]]](val rows: Iterable[T])
Ответ 2
Я пробовал ваш код, и было такое же исключение. Затем я заменил
scala> val listtest = List(row1, row2)
о
scala> val listtest: Iterable[Iterable[Option[Any]]] = List(row1, row2)
listtest: Iterable[Iterable[Option[Any]]] = List(WrappedArray(Some(test), Some(1234)), WrappedArray(Some(test2), Some(12345)))
и он работал нормально:
scala> val test = new Container(listtest)
test: Container = [email protected]
Ответ 3
В первом случае тип listtest
выводится из его определения: List[Array[Some[Any]]]
. @didierd объясняет, почему это не подходит для аргумента конструктора Container
.
Во втором случае аргумент типа List(row1,row2)
выводится из того факта, что он использовался как аргумент конструктора.
Ответ 4
Ваша проблема связана с неявными преобразованиями. Scala не пытается применять неявные преобразования для параметров типа для "содержащихся типов".
Массив не является Итерируемым, поэтому, если вы должны использовать
val row1 = List(Some("test2"), Some(12345))
это работает. Но вы используете Array, поэтому он должен попытаться найти неявное преобразование для Array → Iterable (которое существует), но оно не пытается. Я попытаюсь найти более точную ссылку для этого, но в то же время вы можете увидеть это question.
Если вы явно указываете в качестве Iterable тип строки, то это работает, потому что работает метод ввода типов.
val row1: Iterable[Option[Any]] = Array(Some("test2"), Some(1234))