Locked History Actions

scala/selfTypeAnnotation

自分型アノテーション

自分型アノテーションの用途は二つある。

  • thisの別名を定義する。
  • クラスの機能を分割する。

thisの別名を定義する

object SelfTypeAnnotationSample1 {
  self => // 自分型アノテーション。=>の右側には何もない。「class Internal」を指しているわけでもない。
  
  class Internal {
    def procedure = {
      SelfTypeAnnotationSample1.this.procedure
      self.procedure
    }
  }
  def procedure = {
    println("hello world")
  }
  def main(args: Array[String]) {
    new Internal().procedure
  }
}

この結果は

hello world
hello world

クラスの機能を分割する

一つのクラスの機能を複数のクラスに分割する場合、Javaでは一般に集約を行うが(継承はだめ)、Scalaではtraitによるミックスインという方法がある。 しかし、ミックスインでは機能を集約できるものの、その機能が外側から見えてしまう。

他のtraitの機能をターゲットのクラスにミックスインしつつ、そのtraitの存在を外部に教えないようにするために、自分型アノテーションを使うことができる(こういう説明でよいかわからない)。

以下では、SampleはSomeOtherを自分型として指定するが、これは抽象traitである。 Sampleのオブジェクト生成時に実装traitを指定する。

Sampleを使う側は、このオブジェクトがSomeOtherを利用していることは知らないし、そのメソッドにもアクセスすることはできない。

trait SomeOther { def something }
trait SomeOtherImpl extends SomeOther { def something = { println("something") } }
class Sample {
  self: SomeOther =>
  def test = {
    something
  }
}
object SelfTypeAnnotationSample2 {
  def main(args: Array[String]) {
    object SampleImpl extends  Sample with SomeOtherImpl;
    SampleImpl.test
  }
}

この手法は「Cakeパターン」と呼ばれるDI手法と言われることがあるのだが、新たなパターンというよりも、「手によるコンストラクタ注入」の代用と言った方がよい。