根据以下示例,调用xs.toList.map(_.toBuffer)
成功,但xs.toBuffer.map(_.toBuffer)
失败。但是当使用中间结果执行后者中的步骤时,它会成功。是什么导致这种不一致?
scala> "ab-cd".split("-").toBuffer
res0: scala.collection.mutable.Buffer[String] = ArrayBuffer(ab, cd)
scala> res0.map(_.toBuffer)
res1: scala.collection.mutable.Buffer[scala.collection.mutable.Buffer[Char]] = ArrayBuffer(ArrayBuffer(a, b), ArrayBuffer(c, d))
scala> "ab-cd".split("-").toBuffer.map(_.toBuffer)
<console>:8: error: missing parameter type for expanded function ((x$1) => x$1.toBuffer)
"ab-cd".split("-").toBuffer.map(_.toBuffer)
^
scala> "ab-cd".split("-").toList.map(_.toBuffer)
res3: List[scala.collection.mutable.Buffer[Char]] = List(ArrayBuffer(a, b), ArrayBuffer(c, d))
答案 0 :(得分:5)
查看toBuffer
和toList
的定义:
def toBuffer[A1 >: A]: Buffer[A1]
def toList: List[A]
如您所见,toBuffer
是通用的,而toList
则不是。
这种差异的原因是 - 我相信 - Buffer
是不变的,而List
是协变的。
假设我们有以下类:
class Foo
class Bar extends Foo
由于List
是协变的,您可以在toList
的实例上调用Iterable[Bar]
,并将结果视为List[Foo]
。
如果List
处于不变量,则情况并非如此。
Buffer
不变,如果toBuffer
定义为def toBuffer: Buffer[A]
,您同样无法处理结果
toBuffer
的实例(在Iterable[Bar]
的实例上)作为Buffer[Foo]
的实例(因为Buffer[Bar]
不是Buffer[Foo]
的子类型,与列表不同) 。
但是,通过将toBuffer
声明为def toBuffer[A1 >: A]
(注意添加的类型参数A1
),我们可以让toBuffer
返回Buffer[Foo]
的实例:
我们所需要的只是将A1
explcitly设置为Foo,或者让编译器推断它(如果在期望toBuffer
的站点调用Buffer[Foo]
。)
我认为这解释了toList
和toBuffer
定义不同的原因。
现在问题是toBuffer
是通用的,这会严重影响推理。
执行此操作时:
"ab-cd".split("-").toBuffer
您从未明确说过A1
是String
,但由于"ab-cd".split("-")
明确地指定了类型Array[String]
,编译器知道A
是{{1} }}。
它还知道String
(在A1 >: A
中),并且没有任何进一步的约束,它会推断toBuffer
正好是A1
,换句话说A
。
所以最后整个表达式返回String
。
但是这就是事情:在scala中,类型推断发生在一个表达式中。
当你有Buffer[String]
这样的东西时,你可能会期望scala会推断出一个确切的类型
对于a.b.c
,然后从该推断得到a
的确切类型,最后是a.b
。不是这样。
类型推断 deferred 到整个表达式a.b.c
(请参阅scala规范“6.26.4本地类型推断
“,”案例1:选择“)
因此,回到您的问题,在表达式a.b.c
中,子表达式"ab-cd".split("-").toBuffer.map(_.toBuffer)
不键入"ab-cd".split("-").toBuffer
,而是
它保持类似Buffer[String]
的类型。换句话说,Buffer[A1] forSome A1 >: String
不是固定的,我们只是将约束A1
带到下一步推理。
下一步是A1 >: String
,其中map(_.toBuffer)
定义为map
。此处map[C](f: (B) ⇒ C): Buffer[B]
实际上与B
相同,但此时A1
仍然不完全清楚,我们只知道A1
。
这就是我们的问题。编译器需要知道匿名函数A1 >: String
的确切类型(仅仅因为实例化(_.toBuffer)
需要知道Function1[A,R]
和A
的确切类型,就像任何函数一样通用类型)。
所以你需要以某种方式告诉他,因为它无法完全推断出来。
这意味着您需要执行以下任一操作:
R
或者:
"ab-cd".split("-").toBuffer[String].map(_.toBuffer)