聴きなれない単語が出てきた。トレイトというのは、Scala のオブジェクト指向プログラミングにおけるモジュール化の中心的な概念らしい。トレイトの特徴は次の3つだ。
- 1つのクラスやトレイトに複数のトレイトを継承(ミックスイン)できる
- 直接インスタンス化できない
- クラスパラメータ(コンストラクタの引数)をとることができない
とりあえずは Ruby のモジュールのようなものだと理解した。
トレイトの定義
定義構文はシングルトンオブジェクトの定義に似ていて、object キーワードの代わりに trait キーワードを使う。
scala> trait Hello { | val mes: String = "Hello, World!" | def hello(): Unit = println(mes) | } defined trait Hello
ただし、そのまま使うことはできない。
scala> Hello.hello <console>:12: error: not found: value Hello Hello.hello ^
1つのクラスやトレイトに複数のトレイトを継承(ミックスイン)できる
トレイトの場合は継承というよりミックスインということが多いらしい。extends キーワードを使う継承は1つのクラスしかすることができないけど、with キーワードを使うミックスインは複数できる。
scala> trait TraitA defined trait TraitA scala> trait TraitB defined trait TraitB scala> class ClassA defined class ClassA scala> class ClassB defined class ClassB scala> class ClassC extends ClassA with TraitA with TraitB defined class ClassC
ここで ClassC は ClassA を継承し、TraitA と TraitB をミックスインしている。一方、with キーワードを使っても ClassA と ClassB を継承することはできない。
scala> class ClassD extends CrassA with ClassB <console>:12: error: not found: type CrassA class ClassD extends CrassA with ClassB ^
直接インスタンス化できない
scala> val hello = new Hello <console>:12: error: trait Hello is abstract; cannot be instantiated val hello = new Hello ^
クラスパラメータ(コンストラクタ引数)をとることができない
直接インスタンス化できないんだからコンストラクタ引数をとれないのは当然だけど、じゃあ、定義時に決まらないフィールドはどうすればいいかというと、継承先のクラスで上書きしてやればいい。
scala> trait TraitA { | val name: String | def printName(): Unit = println(name) | } defined trait TraitA scala> class ClassA(val name: String) extends TraitA defined class ClassA scala> val a = new ClassA("Bill") a: ClassA = ClassA@21bc3814 scala> a.printName Bill
TraitA の name フィールドは実装のない抽象フィールドだけど、trait のまえに abstract キーワードをつけなくて構わない。フィールドの実装は ClassA で与えられている。ここで気が付いたけど、1つだけ継承するときはそれがトレイトでも extends キーワードを使う。
さてここまででトレイトの基本を見てきた。つぎはトレイトの機能を見ることにしよう。というところでいったんここまで。