本地分配会影响类型?

时间:2016-06-20 19:31:21

标签: scala generics

在以下示例中,f3可以采用Iterable [Array [Int]]

  def f3(o:Iterable[Iterable[Any]]):Unit = {}

  f3(Iterable(Array(123)))    // OK. Takes Iterable[Array[Int]]

但如果我将Iterable [Array [Int]]赋值给局部变量,则不能:

  val v3 = Iterable(Array(123))
  f3(v3)    // Fails to take Takes Iterable[Array[Int]]

错误:

  Error:(918, 10) type mismatch;
  found   : Iterable[Array[Int]]
  required: Iterable[Iterable[Any]]
  f3(x)

什么是软糖?为什么第一个例子工作,但不是秒。它似乎与嵌套泛型有关:

  def f(o:Iterable[Any]):Unit = {}
  f( Array(123))
  val v1 = Array(123)
  f(v1)  // OK

  def f2(o:Iterable[Any]):Unit = {}
  f2( Iterable(Array(123)))
  val v2 = Array(123)
  f(v2) // OK

使用scala.2.11

2 个答案:

答案 0 :(得分:6)

首先,Array不会扩展Iterable(因为它是Java类型)非常重要。相反,存在从Array[A]Iterable[A]的隐式转换,因此期望的类型很重要。

在第一种情况下:Iterable(Array(123))f3的参数,因此使用预期类型Iterable[Iterable[Any]]进行类型检查。因此Array(123)是预期类型Iterable[Any]的类型。好吧,它的实际类型是Array[Int],编译器会插入转换(因为Iterable[Int]符合Iterable[Any])。所以这实际上是Iterable(array2iterable(Array(123))(我不记得确切的名字)。

在第二种情况下,f3的类型为Iterable[Array[Int]]:没有任何内容可以在val f3 = ...行中触发隐式转换,对吧?当存在从Iterable[Array[Int]]到{{的隐式转换时,从Iterable[Iterable[Int]]Iterable[A](或者更常见的是从Iterable[B]A的隐式转换没有1}}),所以下一行无法编译。您可以自己编写此转换,但它不会有帮助,例如将B转换为Array[Array[Int]]

当然,如果您使用Iterable[Iterable[Int]],则无法触发隐式转换!

答案 1 :(得分:4)

这与Scala中类型推断/统一的工作方式有关。

当您定义变量并省略类型时,Scala会应用尽可能最具体的类型:

scala> val v1 = Iterable(Array(123))
v1: Iterable[Array[Int]] = List(Array(123))

但是,当您指定期望的类型时(例如,通过将值传递给具有已定义参数类型的函数),Scala会将给定参数与预期类型(如果可能)统一起来:

scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123))
v2: Iterable[Iterable[Any]] = List(WrappedArray(123))

由于IntAny的子类型,因此会发生统一并且代码运行正常。

如果您希望函数接受Any子类型的任何内容(没有Scala的统一帮助),则必须明确定义此行为。

修改

虽然我所说的部分属实,但请参阅@ AlexyRomanov的回答以获得更正确的评估。似乎"统一"在ArrayIterable之间实际上是一个隐式转换,当您将Iterable(Array(123))作为参数传递时(参见我在v2声明中的效果)。

假设您有一些代码,其中编译器期望类型B但是找到类型A。在抛出错误之前,编译器会检查类型为A => B的隐式转换函数的集合。如果编译器找到了令人满意的转换,则会自动(并静默)应用转换。

f3不喜欢v1的原因是因为在内部Array[Int]上调用隐式转换为时已晚,并且{{1}不存在现有的隐式转换虽然实现起来很简单,但如下所示:

Iterable[Array[Int]] => Iterable[Iterable[Int]]