看看这个:
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
如何定义Container的第二种方式,但第一种方式不行?这些类型不一样吗?
答案 0 :(得分:13)
这不是错误。 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))
您还可以在其中一个项目上使用类型abscription:
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,List [B]是List [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])
答案 1 :(得分:4)
我尝试了你的代码并且有同样的例外。然后我换了
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
答案 2 :(得分:3)
在第一种情况下,listtest
的类型是从其定义中推断出来的:List[Array[Some[Any]]]
。 @didierd解释了为什么这不是Container
构造函数的参数的合适类型。
在第二种情况下,List(row1,row2)
的类型参数是从它用作构造函数参数的事实推断出来的。
答案 3 :(得分:3)
您的问题在于隐式转化。 Scala不会尝试对“包含类型”的类型参数应用隐式转换。
数组不是Iterable,所以如果你要使用
val row1 = List(Some("test2"), Some(12345))
这是有效的。但是你正在使用Array,所以它应该尝试为Array找到一个隐式转换 - &gt; Iterable(确实存在),但它没有尝试。我会尝试为此找到更好的参考,但与此同时,您可以看到question。
如果您明确指定行的类型为Iterable,那么这是有效的,因为类型推断可以工作。
val row1: Iterable[Option[Any]] = Array(Some("test2"), Some(1234))