LoginSignup
1
0

More than 5 years have passed since last update.

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

Posted at

はじめに

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


トレイト

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

トレイトの様々な機能

菱形継承問題

  • 菱形継承の例

    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となる
  • 解決手法1:override

    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
    
  • 解決手法2:親元のメソッドを利用したoverride

    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> :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> :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を記載
  • extendswith での継承を逆にすると?

    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> :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> :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> :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> :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> :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> :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> :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> :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> :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を持ち合わせているらしい(要調査)
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0