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