Нечетная ошибка ввода в 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))