Scala中奇怪的输入错误

时间:2011-09-02 09:45:12

标签: scala types

看看这个:

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的第二种方式,但第一种方式不行?这些类型不一样吗?

4 个答案:

答案 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))