今日はトレイトの章を。
トレイト
概要
今までどの言語でも見たことのない用語。"trait == 特定とか特色"という意味らしい。
プログラムの分割(モジュール化)と組み立て(合成)は、オブジェクト指向プログラミングでも関数型プログラミングにおいても重要な設計の概念になります。そして、Scalaのオブジェクト指向プログラミングにおけるモジュール化の中心的な概念になるのがトレイトです。
と書いてあるので、Scalaにおける重要な機能なのだろう。
では定義を。
trait <トレイト名> {
(<フィールド定義> | <メソッド定義>)*
}
クラスの定義からコンストラクタ部分を除いたものという感じ。
以下のようなことが特徴として挙げられるとのこと。
上記の3点について見ていく。
複数のトレイトを1つのクラスやトレイトにミックスインできる
Scalaのトレイトはクラスとは異なり、複数のトレイトを1つのクラスやトレイトにミックスイン出来る
trait TraitA trait TraitB class ClassA class ClassB class ClassC extends ClassA with TraitA with TraitB object TraitMixin { def main(args: Array[String]): Unit = { } }
$ scala 20180916_01_trait_mixin.scala # エラーなく実行される
次にwith ClassB
というふうにクラスを指定した場合を見てみる。
class ClassA class ClassB class ClassC extends ClassA with ClassB object TraitMixinNG { def main(args: Array[String]): Unit = { } }
上記のようにwith
にClassB
を指定した場合はエラーになる。
$ scala 20180916_02_trait_mixin_ng.scala 20180916_02_trait_mixin_ng.scala:9: error: class ClassB needs to be a trait to be mixed in class ClassC extends ClassA with ClassB ^ one error found
複数のクラスを継承する必要のある場合はクラスをトレイトにすることで実現が可能。
直接インスタンス化できない
そのままなので、試してみる。
trait TraitA object TraitInstanceNG { def main(args: Array[String]): Unit = { val a = new Trait } }
上記のコードを実行すると以下のようにエラーになる。
$ scala 20180916_03_trait_instance_ng.scala 20180916_03_trait_instance_ng.scala:5: error: trait TraitA is abstract; cannot be instantiated val a = new TraitA ^ one error found
クラスパラメータ(コンストラクタの引数)を取ることができない
これもそのままでクラスでは出来たコンストラクタに引数を持たせるということが出来ない。
trait TraitA(name: String) object TraitNotHasConstructorMemeberNG { def main(args: Array[String]): Unit = { } }
上記のコードを実行すると以下のようにエラーになる。
$ scala 20180916_04_trait_not_has_member_ng.scala /home/dnn-admin/workspace/hw/study/2000_chisel/200_examples/20180916_04_trait_not_has_member_ng.scala:1: error: traits or objects may not have parameters trait TraitA(name: String) ^ one error found
定義で書いたようにトレイト自体はフィールドを持つことが可能だが、コンストラクタ引数が存在しないのでそれを使った値の設定が出来ないことになるが、これも特に問題にはならないらしく以下のように抽象メンバーを持たせることで値を設定可能にすることが出来る。
trait TraitA { val name: String def printName(): Unit = println(name) } class ClassA(val name: String) extends TraitA object TraitAbstractMember { def main(args: Array[String]): Unit = { val a = new ClassA("trait") a.printName() // nameを上書きするような実装を与えることも可能 val a2 = new TraitA { val name = "override" } a2.printName() } }
上記のように定義したTraitA
を継承したクラスを作成しコンストラクタの引数でメンバを上書きするとTraintA
のname
に値を設定することが出来る。
また、val a2
のようにname
を上書きするようにして使用することも可能。このnew Trait {}
を使うとTrait
を継承した無名のクラスを作ってインスタンスすることが出来る。
$ scala 20180916_05_trait_abstract_member.scala trait override
まだこの章は続くのだけど、今日はここまで。