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

# はじめに

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

---

# トレイト

* ざっくりまとめ
    * 菱形継承問題を解決するために
        * (trait->class) 継承先classにてoverride -> 継承の意味合いが薄れる
        * (trait->trait) 継承先traitにてoverride -> 線形化

## トレイトの様々な機能

### 菱形継承問題

* 菱形継承の例

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p() : Unit }
    trait TraitB extends TraitA { def p() : Unit = println("TraitB") }
    trait TraitC extends TraitA { def p() : Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC

    val c = new ClassA
    c.p()

    // Exiting paste mode, now interpreting.

    <console>:17: error: class ClassA inherits conflicting members:
      method p in trait TraitB of type ()Unit  and
      method p in trait TraitC of type ()Unit
    (Note: this can be resolved by declaring an override in class ClassA.)
                  class ClassA extends TraitB with TraitC
                        ^
    ```
    * conflictingとしてerrorとなる

* 解決手法１：override

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit }
    trait TraitB extends TraitA { def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { def p(): Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC { override def p(): Unit = println("ClassA") }

    val c = new ClassA
    c.p()

    // Exiting paste mode, now interpreting.

    ClassA
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    c: ClassA = ClassA@302d20c7
    ```

* 解決手法２：親元のメソッドを利用したoverride

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit }
    trait TraitB extends TraitA { def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { def p(): Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC { override def p(): Unit = super[TraitB].p() }

    val c = new ClassA
    c.p()

    // Exiting paste mode, now interpreting.

    TraitB
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    c: ClassA = ClassA@6587f054

    ---
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit = println("TraitA")}
    trait TraitB extends TraitA { def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { def p(): Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC { override def p(): Unit = super[TraitA].p() }

    val c = new ClassA
    c.p()

    // Exiting paste mode, now interpreting.

    <console>:16: error: TraitA does not name a parent class of class ClassA
                  class ClassA extends TraitB with TraitC { override def p(): Unit = super[TraitA].p() }
                                                                                     ^
    ```

* 双方のメソッドを呼び出す場合

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit}
    trait TraitB extends TraitA { def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { def p(): Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC {
      override def p(): Unit = {
        super[TraitB].p()
        super[TraitC].p()
      }
    }

    val c = new ClassA
    c.p()

    // Exiting paste mode, now interpreting.

    TraitB
    TraitC
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    c: ClassA = ClassA@5362e897
    ```
    * 継承関係が複雑になった場合にすべてを明示的に呼ぶのは大変
    * コンストラクタのように必ず呼び出されるメソッドも有り

### 線形化

* 菱形継承問題の以下を解決する
    * 継承関係が複雑になった場合にすべてを明示的に呼ぶのは大変
    * コンストラクタのように必ず呼び出されるメソッドも有り

* 線形化の例

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit}
    trait TraitB extends TraitA { override def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { override def p(): Unit = println("TraitC") }

    class ClassA extends TraitB with TraitC
    (new ClassA).p

    // Exiting paste mode, now interpreting.

    TraitC
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    ```
    * `TraitB/TraitC` 各々のメソッドに対してoverrideを記載

* `extends` と `with` での継承を逆にすると？

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit}
    trait TraitB extends TraitA { override def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { override def p(): Unit = println("TraitC") }

    class ClassA extends TraitC with TraitB
    (new ClassA).p

    // Exiting paste mode, now interpreting.

    TraitB
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    ```

* 3つ以上継承した場合は？

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit}
    trait TraitB extends TraitA { override def p(): Unit = println("TraitB") }
    trait TraitC extends TraitA { override def p(): Unit = println("TraitC") }
    trait TraitD extends TraitA { override def p(): Unit = println("TraitD") }

    class ClassA extends TraitB with TraitC with TraitD
    (new ClassA).p

    // Exiting paste mode, now interpreting.

    TraitD
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined trait TraitD
    defined class ClassA
    ```
    * 最後に継承したメソッドで上書き

* `super` により線形化された親トレイトを使う

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait TraitA { def p(): Unit = println("TraitA")}
    trait TraitB extends TraitA {
      override def p(): Unit = {
        super.p()
        println("TraitB")
      }
    }
    trait TraitC extends TraitA {
      override def p(): Unit = {
        super.p()
        println("TraitC")
      }
    }

    class ClassA extends TraitB
    class ClassB extends TraitC
    class ClassC extends TraitB with TraitC
    class ClassD extends TraitC with TraitB
    println("=== ClassA ===")
    (new ClassA).p
    println("=== ClassB ===")
    (new ClassB).p
    println("=== ClassC ===")
    (new ClassC).p
    println("=== ClassD ===")
    (new ClassD).p

    // Exiting paste mode, now interpreting.

    === ClassA ===
    TraitA
    TraitB
    === ClassB ===
    TraitA
    TraitC
    === ClassC ===
    TraitA
    TraitB
    TraitC
    === ClassD ===
    TraitA
    TraitC
    TraitB
    defined trait TraitA
    defined trait TraitB
    defined trait TraitC
    defined class ClassA
    defined class ClassB
    defined class ClassC
    defined class ClassD
    ```

### 自分型

* Scalaにはクラスやトレイトの中で自分自身の型にアノテーションを記述することができる機能
* 名称/表記
    * 自分型アノテーション（self type annotations）
    * 自分型（self types）
* 自分型使用例

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit = println("Greeter") }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    // Exiting paste mode, now interpreting.

    defined trait Greeter
    defined trait Robot
    ```

* 先を読まず使ってみた？

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit = println("Greeter") }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }
    class c extends Robot
    (new c).start
    (new c).start.toString

    // Exiting paste mode, now interpreting.

    <console>:16: error: illegal inheritance;
     self-type c does not conform to Robot's selftype Robot with Greeter
                  class c extends Robot
                                  ^
    ```
    * 直接の継承はできない！？

* オブジェクトを実際に作るためには `greet` メソッドを実装したトレイトが必要

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit = println("Greeter") }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
    class c extends Robot with HelloGreeter

    // Exiting paste mode, now interpreting.

    <console>:17: error: overriding method greet in trait Greeter of type => Unit;
     method greet needs `override' modifier
           trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
                                                    ^
    ```
    * overtideしろと出る。なんで！？
        * `def greet: Unit = println("Greeter")` 単純に親traitで宣言していたためっぽい

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
    class c extends Robot with HelloGreeter
    (new c).start

    // Exiting paste mode, now interpreting.

    Hello!
    defined trait Greeter
    defined trait Robot
    defined trait HelloGreeter
    defined class c
    ```

* 直接継承した場合の見え方の違い

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit }

    trait Robot1 {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait Robot2 extends Greeter{
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
    class c1 extends Robot1 with HelloGreeter
    class c2 extends Robot2 with HelloGreeter
    (new c1).start
    (new c2).start
    (new c1).greet
    (new c2).greet

    // Exiting paste mode, now interpreting.

    Hello!
    Hello!
    Hello!
    Hello!
    defined trait Greeter
    defined trait Robot1
    defined trait Robot2
    defined trait HelloGreeter
    defined class c1
    defined class c2
    ```
    * 違いが出ないぞ！？
        * 通常は `(new c1).greet` が失敗するはず

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
    val r:Robot = new Robot with HelloGreeter
    r.greet

    // Exiting paste mode, now interpreting.

    <console>:19: error: value greet is not a member of Robot
                  r.greet
                    ^
    ```
    * Robot traitを直接読みに行った場合のみ出るらしい。何故？

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    trait Greeter { def greet: Unit }

    trait Robot {
      self: Greeter =>
      def start: Unit = greet
      override final def toString = "Robot"
    }

    trait HelloGreeter extends Greeter { def greet: Unit = println("Hello!") }
    val r:Robot = new Robot with HelloGreeter
    r.start

    // Exiting paste mode, now interpreting.

    Hello!
    defined trait Greeter
    defined trait Robot
    defined trait HelloGreeter
    r: Robot = Robot
    ```
    * startは読み出せるので、定義が間違っているわけではない

* `val r:Robot = new Robot with HelloGreeter` で何が生成されている？
    * 内部的にinner classを持ち合わせているらしい（要調査）