はじめに
下記のテキストを通して、基本的な書き方を学びつつ、個人的に気になったことを検証していくメモ代わりです。
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を記載
-
-
extends
とwith
での継承を逆にすると?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
- overtideしろと出る。なんで!?
-
直接継承した場合の見え方の違い
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を持ち合わせているらしい(要調査)