scala,返回类型指南 - 何时更喜欢seq,iterable,traversable

46 scala

您何时选择将给定函数的返回类型键入Seqvs Iterablevs Traversable(或者甚至更深入Seq的层次结构中)?

你是如何做出这个决定的?我们有很多代码Seq默认返回s(通常从数据库查询和连续转换的结果开始).我倾向于在Traversable默认情况下以及Seq在特定期望给定订单时创建返回类型.但我没有充分的理由这样做.

我完全熟悉每个特性的定义,所以请不要回答定义术语.

0__*_*0__ 35

这是一个很好的问题.你必须平衡两个问题:

  • (1)尽量保持API的通用性,以便稍后更改实现
  • (2)给调用者一些有用的操作来执行集合

其中(1)要求你对类型(例如Iterable结束Seq)的具体细节,并且(2)问你相反.

即使返回类型只是Iterable,你仍然可以返回让我们说a Vector,所以如果调用者希望获得额外的功率,它可以只是调用.toSeq.toIndexedSeq在它上面,并且该操作对于a来说是便宜的Vector.

作为平衡的衡量标准,我想补充第三点:

  • (3)使用一种反映数据组织方式的类型.例如,当您可以假设数据确实有序列时,请给出Seq.如果你可以假设不会出现两个相同的对象,那就给出一个Set.等等.

以下是我的经验法则:

  • 尽量只使用一小部分的集合:Set,Map,Seq,IndexedSeq
  • 我经常违反这个先前的规则,List赞成使用Seq.它允许调用者与cons提取器进行模式匹配
  • 使用一成不变的类型仅(例如collection.immutable.Set,collection.immutable.IndexedSeq)
  • 不要使用具体的实现(Vector),而是IndexedSeq使用相同API 的通用类型()
  • 如果你正在封装一个可变结构,只返回Iterator实例,那么调用者可以很容易地生成一个严格的结构,例如通过调用toList
  • 如果您的API很小并且明确调整为"大数据吞吐量",请使用 IndexedSeq

当然,这是我个人的选择,但我希望听起来很清醒.

  • 使用`List`支持`Seq`的基本原理是过时的:"它允许调用者与cons提取器进行模式匹配." 这是无关紧要的,因为在Scala 2.10中为"Seq"添加了类似的提取器. (3认同)
  • 第三点应该是第一点. (2认同)

tks*_*sfz 8

  • Seq在任何地方默认使用。
  • IndexedSeq当您需要通过索引访问时使用。
  • 仅在特殊情况下使用其他任何东西。

这些是“常识”指南。它们简单、实用,并且在平衡原则和性能的同时在实践中运行良好。原则是:

  1. 使用反映数据组织方式的类型(感谢 OP 和 ziggystar)。
  2. 在方法参数和返回类型中使用接口类型。API 的输入和返回类型都受益于通用性的灵活性。

Seq满足这两个原则。如http://docs.scala-lang.org/overviews/collections/seqs.html 中所述

序列是一种具有 [有限] 长度的可迭代对象,其元素具有固定的索引位置,从 0 开始。

90% 的情况下,您的数据是 Seq。

其他注意事项:

  • List是一种实现类型,因此您不应在 API 中使用它。AVector例如不能用作List无需通过转换去。
  • Iterable没有定义lengthIterable跨有限序列和潜在的无限流进行抽象。大多数情况下,人们正在处理有限序列,因此您“有一个长度”,并Seq反映了这一点。通常,您实际上不会使用长度。但是它经常被需要,并且很容易提供,所以使用Seq.

缺点:

这些“常识”约定有一些轻微的缺点。

脚注:


Lui*_*hys 5

使方法的返回类型尽可能具体。然后,如果调用者想要将其保留为 aSuperSpecializedHashMap或将其键入为 a GenTraversableOnce,他们可以。这就是编译器默认推断最具体类型的原因。

  • 对于已实现的方法来说,这是一个很好的建议,但我想补充一点,对于抽象方法来说,这有点棘手。接口上具有非常特定的返回类型可能会强制实现不必要地转换其返回的对象。 (2认同)
  • 问题是,这也将你锁定在那个选择中,这可能不是你想要的。 (2认同)