按照下面的示例,调用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))

最佳答案

查看toBuffertoList的定义:

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]明确设置为Foo,或者让编译器进行推断(如果在需要A1的站点上调用toBuffer)。

我认为这解释了Buffer[Foo]toList定义不同的原因。
现在的问题是toBuffer是通用的,这会严重影响推理。

执行此操作时:

"ab-cd".split("-").toBuffer


您从不会明确地说toBufferA1,但是因为String的类型明确地是"ab-cd".split("-"),所以编译器知道Array[String]A
它还知道String(在A1 >: A中),并且没有任何进一步的约束,它将推断toBuffer恰好是A1,换句话说就是A
因此,最后整个表达式返回一个String

但这就是问题:在scala中,类型推断发生在整个表达式中。
当您有类似Buffer[String]的内容时,您可能希望scala可以推断出确切的类型
对于a.b.c,然后从中推断出a的确切类型,最后是a.b的确切类型。不是这样
类型推断被推迟到整个表达式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的确切类型,就像任何泛型类型一样)。
因此,您需要以某种方式明确地告诉他,因为它无法准确推断出它。

这意味着您需要执行以下任一操作:

"ab-cd".split("-").toBuffer[String].map(_.toBuffer)


要么:

"ab-cd".split("-").toBuffer.map((_:String).toBuffer)

07-24 09:53