在 Scala 中键入伴随对象 [英] Typing over companion object in Scala

查看:54
本文介绍了在 Scala 中键入伴随对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个类及其伴生对象,它们共同具有一些可重用的功能.我已经把伴生对象的功能封装成一个trait,所以现在的情况是这样的

I have a class and its companion object which together have some reusable functionality. I have encapsulated the functionality of the companion object into a trait, so now the situation is like

class Foo {
  import Foo._

  def foo: Quux = bar(this)
}

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

object Foo extends Bar

由于 Foo.foo 是一个可重用的方法,我想把它放到它的 trait 中.

Since Foo.foo is a reusable method, I would like to put it into its trait.

但是我必须找到一种方法来告诉类型检查器,虽然 bar 不是类 Foo 上的方法,但它会在范围内,因为从伴侣对象.我想我需要一些东西,比如能够在类的伴生对象上打字.

But I have to find a way to tell the type checker that, although bar is not a method on class Foo, it will be in scope because imported from the companion object. I think I need something like being able to type over the companion object of a class.

有这样的吗?

推荐答案

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

There are multiple ways to model the abstraction you need in Scala. I will first describe the most simple pattern and analyze your problem, and then I will describe the most complex pattern, which is used in Scala collections.

首先要注意的是,伴生对象是放置您需要在没有类实例的情况下调用的代码的正确位置,同时也是分解您在其中使用的 helpers 的位置实例方法是特征.此外,智能 scala 编译器将为您的 trait 生成一个静态方法,并将所有将使用它的类链接到它.

The first thing to notice is that companion objects are the right place to put code you will need to call without having an instance of your class, while the place to factor out helpers which you use in instance methods are traits. Furthermore, the smart scala compiler will generate a single static method for your trait, and link all the classes which will use it to it.

从您的代码的角度来看,很容易看出如何将其分解为特征,然后通过使用自类型表示法,可以强制特征 FooTrait 只能在以下情况下混合酒吧也是混合的.

From your code point of view, it is very easy to see how it can be factored out into a trait, and then by using the self-type notation, one can enforce that the trait FooTrait can be mixed only if the trait Bar is mixed as well.

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 接口,另一种方法是以下

Please also note, if you do not want to expose the Bar interface through your Foo class, an alternative approach would be the following

  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

当您采用单个类和单个伴生对象时,第二种方法可以很好地工作,但是当您想要开发类层次结构(现在每个类都有一个伴生对象可用)时,这种方法不能很好地扩展,并且您还想强制伴生对象具有与伴生类"相关的某些特征.

This second approach works fine when you take a single class and a single companion objects, but does not scale well when you want to develop a hierarchy of classes where you now there is a companion object available for each of these classes, and you also want to enforce the companion object has certain characteristics with respect to the "companed class".

有一种更复杂的方法,它在 Scala 集合中使用,我强烈建议您不要使用,除非绝对必要.

There is a more complex approach, which is used in Scala collections and which I warmly recommend you not to use unless strictly necessary.

让我们从 GenTraversable 开始:

Let's start from 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
}

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

As you see, the trait defines a companion object, which provides some basic infrastructure for building new collections of the same type (typically for filtering, mapping, and so on).

通过在层次结构中向上,您可以看到 def 伴侣 被细化了:

By going up in the hierarchy, you can see that the def companion is refined:

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
}

如果你在类中冲浪,你就会明白,这个机制是用来保证对于每个具体的集合实现,在作用域中都有一个伴生对象,它具有与类本身相关的某种属性.这是可能的,因为在子类中细化方法的返回类型是合法的.

If you surf among the classes, you will understand that this mechanism is used to guarantee that for each concrete collection implementation, there is a companion in scope with some kind of properties with respect to the class itself. This is possible because it is legal to refine the return type of a method in a child class.

尽管如此,为了正常工作,此机制需要一些手动转换和大量通用参数以及通用签名.

Nevertheless, in order to work correctly this mechanism requires some manual casts and a lot of generic parameters, as well as generic signatures.

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
}

这篇关于在 Scala 中键入伴随对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆