Scala
memo

がばがばscala独学 - トレイト(1)

はじめに

下記のテキストを通して、基本的な書き方を学びつつ、個人的に気になったことを検証していくメモ代わりです。
https://dwango.github.io/scala_text/basic.html
本当にありがとう、dwango様・・・


トレイト

  • ざっくりまとめ
    • extends, with でミックスイン
    • 直接インスタンス化できない( val traitA = new TraitA
      • val traitA = new TraitA {} は無名クラスによる定義なのでインスタンス化ではない
    • 引数をとれないので、class側で上書きするか、抽象メンバーを持たせることで値を渡す
      • class ClassA(val name: String) extends TraitA

トレイトの基礎

プログラムの分割(モジュール化)と組み立て(合成)は、オブジェクト指向プログラミングでも関数型プログラミングにおいても重要な設計の概念になります。

3つの特徴
* 複数のトレイトを1つのクラスやトレイトにミックスインできる
* 直接インスタンス化できない
* クラスパラメータ(コンストラクタの引数)を取ることができない

複数のトレイトを1つのクラスやトレイトにミックスインできる

  • 基本型

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    // trait
    trait TraitA
    trait TraitB
    
    // class
    class ClassA
    class ClassB
    
    // コンパイルできる
    class ClassC extends ClassA with TraitA with TraitB
    
    // Exiting paste mode, now interpreting.
    
    defined trait TraitA
    defined trait TraitB
    defined class ClassA
    defined class ClassB
    defined class ClassC
    
  • classをwith句でくくるとエラー(classのミックスインはできない)

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    // class
    class ClassA
    class ClassB
    
    // コンパイルエラー
    class ClassD extends ClassA with ClassB
    
    // Exiting paste mode, now interpreting.
    
    <console>:14: error: class ClassB needs to be a trait to be mixed in
           class ClassD extends ClassA with ClassB
                                            ^
    

直接インスタンス化できない

  • 直接インスタンス化してみる

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA
    
    object ObjectA {
      val traitA = new TraitA
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:11: error: trait TraitA is abstract; cannot be instantiated
             val traitA = new TraitA
                          ^
    
    • トレイトが単体で使われることをそもそも想定していないための制限
  • with句のみだとエラー

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA
    class ClassA with TraitA
    object ObjectA {
      val classA = new ClassA
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:2: error: ';' expected but 'with' found.
           class ClassA with TraitA
                        ^
    
  • 基本型

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA
    class ClassA extends TraitA
    object ObjectA {
      val classA = new ClassA
    }
    
    // Exiting paste mode, now interpreting.
    
    defined trait TraitA
    defined class ClassA
    defined module ObjectA
    
  • new Trait {} という記法

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    object ObjectA {
      val traitA = new Trait{}
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:8: error: not found: type Trait
             val traitA = new Trait{}
                              ^
    ---
    
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    object ObjectA {
      val traitA = new trait {}
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:2: error: identifier expected but 'trait' found.
             val traitA = new trait {}
                              ^
    ---
    
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    object ObjectA {
      val traitA = new trait TraitA{}
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:2: error: identifier expected but 'trait' found.
             val traitA = new trait TraitA{}
                              ^
    ---
    
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA
    
    object ObjectA {
      val traitA = new TraitA {}
    }
    
    // Exiting paste mode, now interpreting.
    
    defined trait TraitA
    defined module ObjectA
    
    • Traitを継承した無名のクラスを作ってインスタンスを生成する構文
    • トレイトそのものをインスタンス化できているわけではない

クラスパラメータ(コンストラクタの引数)を取ることができない

  • 引数をもたせた場合

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    class ClassA(val name: String)
    trait TraitA(val name: String)
    
    // Exiting paste mode, now interpreting.
    
    <console>:2: error: traits or objects may not have parameters
           trait TraitA(val name: String)
                       ^
    
  • 抽象メンバーを持たせることで値を渡す

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA {
      var name: String // varであまり良くない定義だが、とりあえず
      def printName():Unit = println(name)
      def setName(n: String) = {
        name = n
      }
    }
    class ClassA extends TraitA
    object ObjectA {
      val classA = new ClassA
      classA.setName("hoge")
      classA.printName()
    }
    
    // Exiting paste mode, now interpreting.
    
    <console>:18: error: class ClassA needs to be abstract, since variable name in trait TraitA of type String is not defined
    (Note that variables need to be initialized to be defined)
           class ClassA extends TraitA
                 ^
    ---
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA {
      var name: String // varであまり良くない定義だが、とりあえず
      def printName():Unit = println(name)
      def setName(n: String) = {
        name = n
      }
    }
    class ClassA(var name: String) extends TraitA
    
    object ObjectA {
      val classA = new ClassA("hoge")
    
      def run: Unit = {
        classA.printName()
        classA.setName("hogehoge")
        classA.printName()
      }
    }
    ObjectA.run
    
    // Exiting paste mode, now interpreting.
    
    hoge
    hogehoge
    defined trait TraitA
    defined class ClassA
    defined module ObjectA
    
  • name を上書きするような実装を与える

    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA {
      val name: String
      def printName():Unit = println(name)
    }
    object ObjectA {
      val classA = new TraitA {val name = "hoge"}
    
      def run: Unit = {
        classA.printName()
      }
    }
    ObjectA.run
    
    // Exiting paste mode, now interpreting.
    
    hoge
    defined trait TraitA
    defined module ObjectA
    
    ---
    
    scala> :paste
    // Entering paste mode (ctrl-D to finish)
    
    trait TraitA {
      // もし、定義していなかったら?
      // val name: String
      def printName():Unit = println(name)
    }
    object ObjectA {
      val classA = new TraitA {val name = "hoge"}
    
      def run: Unit = {
        classA.printName()
      }
    }
    ObjectA.run
    
    // Exiting paste mode, now interpreting.
    
    <console>:10: error: not found: value name
                    def printName():Unit = println(name)
                                                   ^