7

Scala の明示的に型付けされた自己参照の最も一般的な使用法は、モジュールの依存関係が次のように宣言される「ケーキ パターン」であると思われます。

class Foo { this: A with B with C =>
  // ...
}

一般に、ケーキのパターンをしばらく無視するとA、型パラメーターなどの任意の型レベルのものを参照できます。BC

class Outer[A, B, C] {
  class Inner { this: A with B with C =>
    // ...
  }
}

... または抽象型メンバー:

class Outer {
  type A
  type B
  type C
  class Inner { this: A with B with C =>
    // ...
  }
}

これらのケースのいずれにおいても、 、 、およびは特性であることが知られていないabstract class Inner extends A with B with CためABと書くことはできませんでした。Cここでは、明示的に型付けされた自己参照が必要です。しかし、トレイトを使ったケーキのパターンしか見たことがありません:

trait A { def a }
trait B { def b }
trait C { def c }
class Foo { this: A with B with C =>
  // ...
}

この場合、代わりにabstract class Foo extends A with B with C直接書くことができますが、私が間違っていなければ同じ意味です。私は正しいですか?そうでない場合、それらはどのように異なりますか。もしそうなら、なぜ誰もが明示的に型付けされた自己参照を使用しているように見えるのですか?

4

2 に答える 2

6

私が見落としていた2つの主な違いがあるようです:

  1. 明示的な self-type アノテーションと simpleextendsキーワードは両方とも、2 つの型の間の「is-a」関係を記述しますが、前者の場合、その関係は外部からは見えません。

    scala> trait T
    defined trait T
    
    scala> class C { this: T => }
    defined class C
    
    scala> implicitly[C <:< T]
    <console>:10: error: Cannot prove that C <:< T.
    

    これは良いことです。というのは、ケーキ パターンでは、「モジュール」オブジェクトが依存するトレイトの 1 つとして誤って多態的に使用されることを望まないからです。

  2. Mushtaq によって明示的に、Daniel によって間接的に指摘されているように自己型注釈を使用すると、依存関係が循環する可能性があります。循環的な依存関係は非常に一般的であり、必ずしも悪いわけではありません (相互に依存するコンポーネントが初期化のために相互に必要としないか、何らかの方法でそれらの間の結び目を結ぶことができると仮定すると)、これは継承に対する自己型アノテーションのもう 1 つの明らかな利点です。

于 2012-07-13T19:46:35.070 に答える
1

継承を使用する場合、初期化の順序を決定します。自己型を使用する場合は、それを開いたままにします。

他にも違いはありますが、ほとんどが実装の詳細で、消えてしまう可能性があると思います。私はそれらのいくつかを知っています。

于 2012-07-13T19:15:09.083 に答える