在Scala中键入伴随对象

时间:2012-08-29 13:28:28

标签: scala types companion-object

我有一个类及其伴随对象,它们共同具有一些可重用的功能。我已将伴随对象的功能封装成特征,所以现在情况就像

class Foo {
  import Foo._

  def foo: Quux = bar(this)
}

trait Bar {
  def bar(f: Foo): Quux = frobnicate(f)
}

object Foo extends Bar

由于Foo.foo是一种可重复使用的方法,我想把它放到它的特性中。

但是我必须找到一种方法告诉类型检查器,虽然bar不是类Foo上的方法,但它将在范围内,因为从伴随对象导入。我想我需要能够键入类的伴随对象。

有类似的东西吗?

2 个答案:

答案 0 :(得分:8)

有多种方法可以为Scala中的抽象建模。我将首先描述最简单的模式并分析您的问题,然后我将描述最复杂的模式,它在Scala集合中使用。

首先要注意的是,伴侣对象是放置您需要调用的代码的正确位置,而不需要您的类的实例,而您可以将帮助放在哪里实例方法是特征。此外,智能scala编译器将为您的特征生成单个静态方法,并链接将使用它的所有类。

从你的代码的角度来看,很容易看出它如何被分解成一个特征,然后通过使用自我类型表示法,可以强制特征FooTrait只有在特征时才可以混合酒吧也是混合的。

class Foo extends FooTrait with Bar 

  trait FooTrait {
    self:Bar =>
    def foo: Quux = bar(this)
  }

  trait Bar {
    def bar(f: Foo): Quux = frobnicate(f)
  }

请注意,如果您不希望通过Foo类公开Bar界面,则可采用以下替代方法

  class Foo extends FooTrait {
    protected val barrer = Foo
  }

  trait FooTrait {
    protected val barrer:Bar
    def foo: Quux = barrer.bar(this)
  }

  trait Bar {
    def bar(f: Foo): Quux = frobnicate(f)
  }

  object Foo extends Bar

当您使用单个类和单个伴随对象时,第二种方法可以正常工作,但是当您想要开发类的层次结构时,这种方法不能很好地扩展,现在您可以为这些类中的每个类提供一个伴随对象,并且你也想强制执行伴侣对象具有“比较阶级”的某些特征。

有一种更复杂的方法,在Scala集合中使用,我强烈建议您除非严格必要,否则不要使用。

让我们从GenTraversable开始:

trait GenTraversable[+A]
extends GenTraversableLike[A, GenTraversable[A]]
   with GenTraversableOnce[A]
   with GenericTraversableTemplate[A, GenTraversable]
{
  def seq: Traversable[A]
  def companion: GenericCompanion[GenTraversable] = GenTraversable
}


object GenTraversable extends GenTraversableFactory[GenTraversable] {
  implicit def canBuildFrom[A] = new GenericCanBuildFrom[A]
  def newBuilder[A] = Traversable.newBuilder
}

如您所见,该特征定义了一个伴随对象,它为构建相同类型的新集合提供了一些基本的基础结构(通常用于过滤,映射等)。

通过在层次结构中上升,您可以看到def companion已经完善:

trait GenIterable[+A]
extends GenIterableLike[A, GenIterable[A]]
   with GenTraversable[A]
   with GenericTraversableTemplate[A, GenIterable]
{
  def seq: Iterable[A]
  override def companion: GenericCompanion[GenIterable] = GenIterable
}


object GenIterable extends GenTraversableFactory[GenIterable] {
  implicit def canBuildFrom[A] = new GenericCanBuildFrom[A]
  def newBuilder[A] = Iterable.newBuilder
}

如果你在类之间浏览,你会明白这个机制用于保证对于每个具体的集合实现,在范围内有一个伴随着类本身的某种属性。这是可能的,因为在子类中细化方法的返回类型是合法的。

尽管如此,为了正常工作,这种机制需要一些手动转换和许多通用参数,以及通用签名。

trait GenericTraversableTemplate[+A, +CC[X] <: GenTraversable[X]] extends HasNewBuilder[A, CC[A] @uncheckedVariance] {
 protected[this] def newBuilder: Builder[A, CC[A]] = companion.newBuilder[A]

  /** The generic builder that builds instances of $Coll
   *  at arbitrary element types.
   */
  def genericBuilder[B]: Builder[B, CC[B]] = companion.newBuilder[B]

  private def sequential: TraversableOnce[A] =this.asInstanceOf[GenTraversableOnce[A]].seq
// other code
}

答案 1 :(得分:2)

也许您需要self type

trait Foo { self: Bar => // This says that anything that will mix in Foo
                         // must mix in Bar too.
  def foo: Quux = bar(this)
}

trait Bar {
  def bar(f: Foo): Quux = frobnicate(f)
}

object Foo extends Foo with Bar