什么时候应该使用.empty而不是单例空实例



在Scala中使用集合时,通常需要使用集合的空实例作为基准情况。因为默认的空实例用类型参数Nothing扩展集合类型类,所以直接使用它们有时会破坏类型推断。例如:

scala> List(1, 2, 3).foldLeft(Nil)((x, y) => x :+ y.toString())
<console>:8: error: type mismatch;
 found   : List[String]
 required: scala.collection.immutable.Nil.type
              List(1, 2, 3).foldLeft(Nil)((x, y) => x :+ y.toString())
                                                  ^

失败,但以下两个更正成功:

scala> List(1, 2, 3).foldLeft(Nil: List[String])((x, y) => x :+ y.toString())
res9: List[String] = List(1, 2, 3)
scala> List(1, 2, 3).foldLeft(List.empty[String])((x, y) => x :+ y.toString())
res10: List[String] = List(1, 2, 3)

另一个我遇到类似困境的地方是定义默认参数。这些是我能想到的唯一的例子,但我知道我也见过其他的例子。提供正确类型提示的一种方法是否优于另一种方法?有没有各自有利的地方?

我倾向于结合使用Nil(或None),并告诉类型参数化方法给定的特定用例的类型(如Kigyo) 。虽然我认为使用显式类型注释对于给定的用例同样是可以的。但我认为有用例,你想坚持使用.empty,例如,如果你试图调用Nil: List[String]的方法,你首先必须把它包装在大括号中,所以这是2个额外的字符!!

现在使用.empty的另一个参数是与整个集合层次结构的一致性。例如,你不能做Nil: Set[String],也不能做Nil: Option[String],但你可以做Set.empty[String]Option.empty[String]。所以,除非你真的确定你的代码永远不会被重构到其他集合中,否则你应该使用.empty,因为它需要更少的东西来重构。此外,保持一致通常很好,对吧?:)

公平地说,我经常使用NilNone,因为我经常很确定我永远不想使用Set或其他东西,事实上,我想说,当你真的确定你只打算处理列表时,最好使用Nil,因为它告诉代码的读者"我真的真的在这里处理列表"。

最后,您可以使用.empty和duck类型做一些很酷的事情,看看这个简单的例子:

def printEmptyThing[K[_], T <: {def empty[A] : K[A]}](c: T): Unit = 
  println("thing = " + c.empty[String])
printEmptyThing[List, List.type](List)
printEmptyThing[Option, Option.type](Option)
printEmptyThing[Set, Set.type](Set)

将打印:

> thing = List()
> thing = None
> thing = Set()

最新更新