您何时选择将给定函数的返回类型键入为Seq
vs Iterable
vs Traversable
(或者甚至在Seq
的层次结构中更深)?
你如何做出这个决定?我们有很多Seq
默认返回 s 的代码(通常从数据库查询和连续转换的结果开始)。我倾向于在Traversable
默认情况下以及Seq
在特别期望给定订单时创建返回类型。但我没有这样做的充分理由。
我非常熟悉每个特征的定义,所以请不要用定义术语来回答。
您何时选择将给定函数的返回类型键入为Seq
vs Iterable
vs Traversable
(或者甚至在Seq
的层次结构中更深)?
你如何做出这个决定?我们有很多Seq
默认返回 s 的代码(通常从数据库查询和连续转换的结果开始)。我倾向于在Traversable
默认情况下以及Seq
在特别期望给定订单时创建返回类型。但我没有这样做的充分理由。
我非常熟悉每个特征的定义,所以请不要用定义术语来回答。
这是一个很好的问题。您必须平衡两个问题:
其中 (1) 要求您尽可能少地具体说明类型(例如Iterable
over Seq
),而 (2) 要求您相反。
即使返回类型是 just Iterable
,你仍然可以返回让我们说 a Vector
,所以如果调用者希望获得额外的权力,它可以只调用.toSeq
or .toIndexedSeq
,并且该操作对于 a 来说很便宜Vector
。
作为衡量平衡的标准,我要补充第三点:
Seq
. 如果您可以假设不可能出现两个相等的对象,请给出Set
. 等等。以下是我的经验法则:
Set
, Map
, Seq
,IndexedSeq
List
支持Seq
. 它允许调用者使用 cons 提取器进行模式匹配collection.immutable.Set
,collection.immutable.IndexedSeq
)Vector
),而是使用通用类型 (IndexedSeq
),而是使用提供相同 APIIterator
实例,那么调用者可以轻松地生成一个严格的结构,例如通过调用toList
它IndexedSeq
当然,这是我个人的选择,但我希望这听起来很理智。
Seq
。IndexedSeq
当您需要按索引访问时使用。这些是“常识”准则。它们简单、实用,并且在实践中运行良好,同时平衡了原则和性能。原则是:
Seq
满足这两个原则。如http://docs.scala-lang.org/overviews/collections/seqs.html中所述:
序列是一种具有 [有限] 长度且其元素具有固定索引位置的可迭代对象,从 0 开始。
90% 的情况下,您的数据是 Seq。
其他注意事项:
List
是一种实现类型,因此您不应在 API 中使用它。例如,如果不经过转换,就不能将AVector
用作 a 。List
Iterable
没有定义length
。 Iterable
跨有限序列和潜在无限流的抽象。大多数时候,人们都在处理有限序列,所以你“有一个长度”,并Seq
反映了这一点。通常,您实际上不会使用长度。但它经常需要,而且很容易提供,所以使用Seq
.缺点:
这些“常识”约定有一些轻微的缺点。
case head :: tail => ...
. 您可以使用:+
和,如此处+:
所述。然而,重要的是,匹配仍然可以按照Scala:模式匹配 Seq[Nothing]中的描述进行。Nil
脚注:
Map
这里讨论,因为这个问题,明智地,并没有问到它。使您的方法的返回类型尽可能具体。然后,如果调用者想要将其保留为 aSuperSpecializedHashMap
或将其键入为 a GenTraversableOnce
,他们可以。这就是编译器默认推断出最具体的类型的原因。
我遵循的经验法则是,根据实现,使返回类型尽可能具体,参数类型尽可能通用。这是一个易于遵循的规则,它以最大的自由度为您提供一致的类型属性保证。
map
比如说,如果你有一个函数实现,它只是用,filter
或fold
- 那些在 trait 中实现的方法来遍历数据结构Traversable
,你可以期望它在任何类型的输入集合上同样执行 - 无论是 a List
,Vector
,HashSet
甚至是 a HashMap
,因此您的输入参数应指定为Traversable[T]
。函数输出类型的选择应该只取决于它的实现:在这种情况下也应该Traversable
如此。但是,如果在您的函数中使用toList
,toSeq
或之类的方法将此数据结构强制为某些更具体的类型toSet
,则应指定适当的类型。注意到实现和返回类型之间的一致性了吗?
如果您的函数通过索引访问 input 的元素,则应将 input 指定为IndexedSeq
,因为它是最通用的类型,可为您提供有效实现 method 的保证apply
。
在抽象成员的情况下,相同的规则适用,唯一的区别是您应该根据您计划如何使用它们而不是实现来指定返回类型,因此它们通常比实现更通用。分类选择Seq
,Set
或Map
是最值得期待的。
遵循这条规则,您可以保护自己免受非常常见的瓶颈情况的影响,例如,当项目被附加到List
或被contains
调用 aSeq
而不是 aSet
时,您的程序仍然具有很好的自由度,并且在类型选择方面是一致的。
快速说明:在 Scala 2.13.x 中,Traversable
是不可能的。 Iterable
更普遍,并且确定向前发展二元性是不合理的。 Iterable
现在位于集合层次结构的顶部并且Traversable
已被弃用。