【问题标题】:toSet and type inferencetoSet 和类型推断
【发布时间】:2014-10-07 14:05:33
【问题描述】:

有人可以解释为什么以下内容不起作用。当我做toSet 时,不知何故失去了编译类型推断的一些信息,但我不明白为什么。

scala> case class Foo(id: Int, name: String)
defined class Foo

scala> val ids = List(1,2,3)
ids: List[Int] = List(1, 2, 3)

scala> ids.toSet.map(Foo(_, "bar"))
<console>:11: error: missing parameter type for expanded function ((x$1) => Foo(x$1, "bar"))
              ids.toSet.map(Foo(_, "bar"))
                                ^

scala> ids.map(Foo(_, "bar")).toSet
res1: scala.collection.immutable.Set[Foo] = Set(Foo(1,bar), Foo(2,bar), Foo(3,bar))

【问题讨论】:

  • 似乎编译器需要一些帮助来明确类型,ids.toSet.map(Foo(_: Int, "bar"))
  • 是的,但是为什么当我在地图之后执行 toSet 时编译器不需要这些信息??
  • 令人困惑的是,这行得通,val a = ids.toSet ; a.map(Foo(_, "bar"))
  • 在这里考虑一个类似的错误:stackoverflow.com/q/4701761/3189923
  • @enzyme:尽管表面上很相似,但这并不是真正的类似错误。在 OP 的情况下,错误与 toSet 的使用有关。请注意,将 toSet 替换为 toSeq 时,它编译得很好。

标签: scala


【解决方案1】:

假设我有以下内容:

trait Pet {
  def name: String
}

case class Dog(name: String) extends Pet

val someDogs: List[Dog] = List(Dog("Fido"), Dog("Rover"), Dog("Sam"))

Set 在其类型参数中不是协变的,但 List 是。这意味着如果我有一个List[Dog],我也有一个List[Pet],但Set[Dog]不是Set[Pet]。为方便起见,Scala 允许您在从List(或其他集合类型)到Set 的转换期间通过在toSet 上提供显式类型参数来向上转换。当你写val a = ids.toSet; a.map(...)时,会推断出这个类型参数,你就可以了。另一方面,当你写 ids.toSet.map(...) 时,它并没有被推断出来,而且你很不走运。

这允许以下工作:

scala> val twoPetSet: Set[Pet] = someDogs.toSet.take(2)
twoPetSet: Set[Pet] = Set(Dog(Fido), Dog(Rover))

虽然不是这样:

scala> val allDogSet: Set[Dog] = someDogs.toSet
allDogSet: Set[Dog] = Set(Dog(Fido), Dog(Rover), Dog(Sam))

scala> val twoPetSet: Set[Pet] = allDogSet.take(2)
<console>:14: error: type mismatch;
 found   : scala.collection.immutable.Set[Dog]
 required: Set[Pet]
Note: Dog <: Pet, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Pet`. (SLS 3.2.10)
       val twoPetSet: Set[Pet] = allDogSet.take(2)
                                               ^

这值得混淆吗?我不知道。但这有点道理,而且这是 Collections API 设计人员为 toSet 做出的决定,所以我们坚持下去。

【讨论】:

  • 一个令人困惑的事情是,无法推断toSet 的类型被编译器表示为无法推断Foo(_, "bar") 的类型。推理似乎在任一方向起作用:ids.toSet[Int].map(Foo(_, "bar")) 没有抱怨,ids.toSet.map(Foo(_: Int, "bar")) 也没有。
  • @Joe:问题是map 需要知道其参数的参数类型。如果调用它的集合的类型未知或尚未推断,则必须明确提供该类型。
猜你喜欢
  • 1970-01-01
  • 2017-08-28
  • 1970-01-01
  • 2014-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多