【问题标题】:Create an instance of a generic type in Scala?在 Scala 中创建泛型类型的实例?
【发布时间】:2016-09-20 07:36:24
【问题描述】:

我正在尝试使用 Scala 进行一些通用编程,并试图弄清楚如何创建 CC 类型的类的实例,如下面的代码所述。我已经定义了以下抽象特征......

/** Trait defining the interface for an `OrderBook`.
  *
  * @tparam O type of `Order` stored in the order book.
  * @tparam CC type of collection used to store `Order` instances.
  */
trait OrderBook[O <: Order, CC <: collection.GenMap[UUID, O]] {

  /** All `Orders` contained in an `OrderBook` should be for the same `Tradable`. */
  def tradable: Tradable

  /** Add an `Order` to the `OrderBook`.
    *
    * @param order the `Order` that should be added to the `OrderBook`.
    */
  def add(order: O): Unit

  /** Filter the `OrderBook` and return those `Order` instances satisfying the given predicate.
    *
    * @param p predicate defining desirable `Order` characteristics.
    * @return collection of `Order` instances satisfying the given predicate.
    */
  def filter(p: (O) => Boolean): Option[collection.GenIterable[O]] = {
    val filteredOrders = existingOrders.filter { case (_, order) => p(order) }
    if (filteredOrders.nonEmpty) Some(filteredOrders.values) else None
  }

  /** Find the first `Order` in the `OrderBook` that satisfies the given predicate.
    *
    * @param p predicate defining desirable `Order` characteristics.
    * @return `None` if no `Order` in the `OrderBook` satisfies the predicate; `Some(order)` otherwise.
    */
  def find(p: (O) => Boolean): Option[O] = existingOrders.find { case (_, order) => p(order) } match {
    case Some((_, order)) => Some(order)
    case None => None
  }

  /** Return the head `Order` of the `OrderBook`.
    *
    * @return `None` if the `OrderBook` is empty; `Some(order)` otherwise.
    */
  def headOption: Option[O] = existingOrders.values.headOption

  /** Remove and return the head `Order` of the `OrderBook`.
    *
    * @return `None` if the `OrderBook` is empty; `Some(order)` otherwise.
    */
  def remove(): Option[O] = {
    headOption match {
      case Some(order) => remove(order.uuid)
      case None => None
    }
  }

  /** Remove and return an existing `Order` from the `OrderBook`.
    *
    * @param uuid the `UUID` for the order that should be removed from the `OrderBook`.
    * @return `None` if the `uuid` is not found in the `OrderBook`; `Some(order)` otherwise.
    */
  def remove(uuid: UUID): Option[O]

  /* Underlying collection of `Order` instances. */
  protected def existingOrders: CC

}

...然后在伴生对象中隐藏此 trait 的实现,以强制希望创建自己的特殊 OrderBook 类的用户使用 trait 中定义的接口,而不是具体实现的子类。这是伴随对象...

object OrderBook {

  import scala.collection.mutable
  import scala.collection.parallel

  def apply[O <: Order, CC <: mutable.Map[UUID, O]](tradable: Tradable): OrderBook[O, CC] = {
    new MutableOrderBook[O, CC](tradable)
  }

  def apply[O <: Order, CC <: parallel.mutable.ParMap[UUID, O]](tradable: Tradable): OrderBook[O, CC] = {
    new ParallelMutableOrderBook[O, CC](tradable)
  }

  private class MutableOrderBook[O <: Order, CC <: mutable.Map[UUID, O]](val tradable: Tradable)
    extends OrderBook[O, CC] {

    /** Add an `Order` to the `OrderBook`.
      *
      * @param order the `Order` that should be added to the `OrderBook`.
      */
    def add(order: O): Unit = {
      require(order.tradable == tradable)  // can be disabled by compiler?
      existingOrders(order.uuid) = order
    }

    /** Remove and return an existing `Order` from the `OrderBook`.
      *
      * @param uuid the `UUID` for the order that should be removed from the `OrderBook`.
      * @return `None` if the `uuid` is not found in the `OrderBook`; `Some(order)` otherwise.
      */
    def remove(uuid: UUID): Option[O] = existingOrders.remove(uuid)

    /* Underlying collection of `Order` instances. */
    protected val existingOrders: CC = ??? // I want this to be an empty instance of type CC!

  }

  private class ParallelMutableOrderBook[O <: Order, CC <: parallel.mutable.ParMap[UUID, O]](val tradable: Tradable)
    extends OrderBook[O, CC] {
      /// details omitted for brevity!
  }

}

我想弄清楚如何在我的MutableOrderBook 实现中创建一个CC 类型的空实例。我希望这可以不加反思地完成。如果需要反射,我愿意接受有关如何避免在此用例中使用反射的建议。想法?

【问题讨论】:

  • 你有什么理由在特征中有CC &lt;: collection.GenMap[UUID, O]],在对象中有CC &lt;: mutable.Map[UUID, O]
  • @samer 我想重载 apply 方法,以便伴随对象成为工厂。类型边界是我计划重载的。我已经更新了对象来证明这一点......

标签: scala generics scala-collections


【解决方案1】:

您可以使用scala.collection.generic.CanBuildFrom[-From, -Elem, +To],它就是专门针对这个问题设计的。

private class MutableOrderBook[O <: Order, CC <: mutable.Map[UUID, O]]
    (val tradable: Tradable)(implicit cbf: CanBuildFrom[Nothing, Nothing, CC]) {
    //give me a CanBuildFrom, which can build a CC  out of Nothing.

    ...

    val existingOrders: CC = cbf().result
}

scala 集合库在内部大量使用 CanBuildFrom,例如在 mapflatMap 中构建正确的集合。欲了解更多信息,请阅读this 答案。

【讨论】:

  • 我正在尝试实施您的解决方案。但似乎无法获取代码进行编译。如果我理解正确,这种情况下的Elem 类型应该是(UUID, O)。但是From 类型使用什么?我需要创建自己的定制CanBuildFrom 还是应该能够使用已经在范围内隐式定义的?我认为应该已经隐含了从任何元素集合`(UUID,O)中构建mutable.Map[UUID, O]的子类型的范围。
  • 我们不关心ToFrom,所以我使用Nothing。由于逆变,任何以 CC 为 To 的 CanBuildFrom 都是 CanBuildFrom[Nothing,Nothing,CC] 的子类型,将满足我们的需求。换句话说,我们并不关心它可以构建什么样的元素,因为我们实际上不需要向构建器添加任何东西。你得到什么错误?
  • 我收到错误消息,说编译器不知道如何使用Nothing 的元素从Nothing 中构建CC 类型的集合。
  • 如果像this那样做应该可以工作,基本上是添加一个CanBuildFrom[_, _, CC]参数来应用,并在调用apply时显式指定类型参数。
猜你喜欢
  • 2010-09-09
  • 2014-05-31
  • 1970-01-01
  • 1970-01-01
  • 2020-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多