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
この2番目のアプローチは、単一のクラスと単一のコンパニオンオブジェクトを使用する場合は正常に機能しますが、これらの各クラスで使用できるコンパニオンオブジェクトが存在するクラスの階層を開発する場合は、適切に拡張できません。コンパニオンオブジェクトを強制することには、「コンパニオンクラス」に関して特定の特性があります。
より複雑なアプローチがあります。これは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
}