在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
,因为它需要更少的东西来重构。此外,保持一致通常很好,对吧?:)
公平地说,我经常使用Nil
和None
,因为我经常很确定我永远不想使用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()