Странная ошибка ввода в Scala - PullRequest
10 голосов
/ 02 сентября 2011

Взгляните на это:

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= Container@600a08

Почему определение контейнера работает вторым способом, а первый - нет?Разве типы не одинаковы?

Ответы [ 4 ]

13 голосов
/ 02 сентября 2011

Это не ошибка. Array не является ковариантным. B, являющийся подтипом B, не делает Array [B] подтипом Array [A]. Это противоречит Java, где B [] является подтипом A [], что является необоснованным:

A[] b = new B[1];
b[0] = new (A);
-> ArrayStoreException

Таким образом, ваш Array [Some [Any]] не является Array [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 является ковариантным (List [B] - это List [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])
4 голосов
/ 02 сентября 2011

Я попробовал ваш код, и было то же исключение. Затем я заменил

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 = Container@a75974
3 голосов
/ 02 сентября 2011

Ваша проблема с неявными преобразованиями. Scala не пытается применять неявные преобразования для параметров типа для «содержащихся типов».

Массив не является итерируемым, поэтому, если бы вы использовали

val row1 = List(Some("test2"), Some(12345))

это работает. Но вы используете Array, поэтому он должен попытаться найти неявное преобразование для Array -> Iterable (который существует), но он не пытается. Я постараюсь найти лучшую ссылку для этого, но пока вы можете видеть этот вопрос .

Если вы явно указываете в качестве Iterable тип строки, то это работает, потому что работает вывод типа.

val row1: Iterable[Option[Any]] = Array(Some("test2"), Some(1234))
3 голосов
/ 02 сентября 2011

В первом случае тип listtest выводится из его определения: List[Array[Some[Any]]].@didierd объясняет, почему это не подходящий тип для аргумента конструктора Container.

Во втором случае аргумент типа List(row1,row2) выводится из того факта, что он используется в качестве аргумента конструктора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...