2012年7月8日日曜日

[Scala] trait

Scala では Java でいうところの Interface は trait という。
trait を少し使ってみる。

object TraitSample {
    def main(args : Array[String]) {
        val eng : English  = new English();
        println(eng.hello());
    }
}

trait Greetable {
  def hello() : String;
}

class English() extends AnyRef with Greetable {
  override def hello() : String = "hello.";
}

hello.

こんな感じ。「extends AnyRef」は省略できるのだが、派生クラスを省略した場合には実装する最初の trait は extends で書かないとエラーになる。

class English() extends Greetable {
  override def hello() : String = "hello.";
}

こうなると Greetable が trait なのだか class なのだか分からないのでちょっと嫌な感じ。
ちなみに、 AnyRefは参照型の全オブジェクトの親クラス。
詳しくはこちら ( 日経ITプロ Scala言語を探検する 4 ) を参照。

Anyすべてのオブジェクトの親クラス
  AnyValすべてのプリミティブ型(Intとか)の親クラス
  AnyRefすべての参照型オブジェクトの親クラス(上以外)

上の例の override は省略することができる。Scala では、「すでに実装のあるメソッド」をオーバーライドする場合には override を指定しなければならない。もちろんオーバーライドでないのに override を指定するとエラーになるので、実装のない抽象メソッドを実装する場合にも間違い防止に付けておいたほうがいい。

trait は実装を持つこともできる。

...

trait Greetable {
  def hello() : String = "hello.";
}

class English() extends AnyRef with Greetable {
}

hello.

もうこうなると 抽象 class でも trait でもどちらでもよい気がしてくる。
ただ、これはこれで便利で、汎用的な機能をいろいろ trait に実装できるので無駄な実装がかなり減る気がする。

object TraitSample {
    def main(args : Array[String]) {
        val eng : English  = new English();
        println(eng.hello());
        println(eng.bye());
    }
}

trait Greetable {
  def hello() : String = "hello.";
}

trait Byeable {
  def bye() : String = "bye.";
}

class English() extends AnyRef with Greetable with Byeable {
}

hello. bye.

もはや多重継承。実装を宣言している2つの trait が同じメソッドを持っている場合はどうなるのだろうか?
まず実装を持っている trait がひとつだけの場合には何も問題ない。普通に動く。

両方とも実装を持っていた場合にはコンパイルエラーになる。
上のコードで Greetable に Byeable と同じ bye() メソッドを定義するとこのようになる。

overriding method bye in trait Greetable of type ()String; method bye in trait Byeable of type ()String needs `override' modifier

override をつければいいのか? とはいっても両 trait とも何かを override しているわけではないので、trait 側にoverride をつけるわけにはいかない。というわけなので、実装するクラス側で override してやるしかない。
object TraitSample {
    def main(args : Array[String]) {
        val eng : English  = new English();
        println(eng.hello());
        println(eng.bye());
    }
}

trait Greetable {
  def hello() : String = "hello.";
  def bye() : String = "bye [Greetable].";
}

trait Byeable {
  def bye() : String = "bye [Byeable].";
}

class English() extends AnyRef with Greetable with Byeable {
  override def bye() : String = super.bye();
}

hello. bye [Byeable].

super で呼び出されたのは Byeable のほうだった。ちなみに、
...
class English() extends AnyRef with Byeable with Greetable {
  override def bye() : String = super.bye();
}

hello. bye [Greetable].

あと勝ちだった。

0 件のコメント:

コメントを投稿