Scalaのトレイトでプログラマをミックスインしてやんよスケーラブルで関数型でオブジェクト指向なScala入門(7)(3/3 ページ)

» 2012年06月20日 00時00分 公開
[中村修太クラスメソッド株式会社]
前のページへ 1|2|3       

トレイトで重要な指定順序

 ここで、もう1つトレイトを重ねてみましょう。仕事を半分の時間で終わらせることができる、Agilerトレイトを定義してみます。

trait Agiler extends Engineer{
    println("trait Agiler constructor")
 
    abstract override def work(time:Int) = {
        println("Agiler#work start")
        super.work(time / 2)
        println("Agiler#work end")
    }
}

 ProgrammerとAgilerの2つのトレイトをミックスインしてworkメソッドを実行してみましょう。

 scala> val p = new Person with Programmer with Agiler
 class Engineer constructor
 class Person constructor
 trait Programmer constructor
 trait Agiler constructor
 p: Person with Programmer with Agiler = $anon$1@21e85538
 
 scala> p.work(60)
 Agiler#work start
 Programmer#work start
 Person#work start
 1つのタスクを15分で行います
 Person#work end
 Programmer#work end
 Agiler#work end

 トレイトのコンストラクタは指定順序で呼ばれ、Agiler→Programmerの順にメソッドが呼ばれています。トレイトにおいて、その指定順序は重要です。先ほどはProgrammer、Agilerの順番でトレイトをミックスインしていましたが、これを逆にしてみました。

 scala> val p = new Person with Agiler with Programmer
 class Engineer constructor
 class Person constructor
 trait Agiler constructor
 trait Programmer constructor
 p: Person with Agiler with Programmer = $anon$1@5f91e28c
 scala> p.work(60)
 Programmer#work start
 Agiler#work start
 Person#work start
 1つのタスクを22分で行います
 Person#work end
 Agiler#work end
 Programmer#work end

 コンストラクタの実行順序とトレイトが実行される順番が変わり、実行結果も変わっています。積み重ねたトレイトは常に指定した右から順番に実行されるわけではありませんが、だいたい指定した右から順番に実行されます。

トレイトの「線形化」

 先ほど、「トレイトを積み重ねたときの実行順序はだいたい右から実行される」と言いましたが、しっかりと実行される順序は決まっています。C++などの一般的な多重継承では、superによって呼び出されるメソッドはsuperをどこで呼び出しているかで決まりますが、Scalaのトレイトの場合「トレイトを線形化(linearization)した結果」によって決まります。

 newを使用してクラスのインスタンスを作成したとき、Scalaは自身のクラス、継承されているクラスやトレイトすべてを1本の直線的順序に並べます。これを「線形化」と呼びます。この線形化されたクラス/トレイトのどこかでsuperを使用した場合、線形化によって並べられた順序でクラスやトレイトに属するメソッドが呼び出されるようになります。

 こうして、すべてのメソッドやsuperが適切に呼び出された結果、積み重ね可能な変更が実現します。ポイントは、「線形化では、あるクラスはすべてのスーパークラス、ミックスインされているすべてのトレイトよりも前に配置される」ということです。superを呼び出すメソッドを記述したとき、そのメソッドはスーパークラスやミックスインされたトレイトの振る舞いを変更します。

 では、サンプルを見てみましょう。サンプルではベースとなるEngineerクラスを基に、DesignerトレイトとArchitectトレイトをミックスインしているPersonクラスを使用します。

class Engineer
trait Designer extends Engineer
trait Programmer extends Engineer
trait Architect extends Programmer
 
class Person extends Engineer with Designer with Architect

 クラスとトレイトの継承関係は図のようになっています。

図 クラスとトレイトの継承関係 図 クラスとトレイトの継承関係

 では順を追って線形化していきましょう。Personの線形化順序は後ろから前に決まっていきます。そのため、線形化の最後はPersonのスーパークラスのEngineerになります。Engineerは明示的な継承やトレイトのミックスインをしていないので、デフォルトのAnyRefを継承します。AnyRefはAnyを継承しているので、Engineerの線形化は、以下です。

Engineer→AnyRef→Any

 次に線形化されるのは、最初(Archtectの左側)にミックスインしたDesignerトレイトです。すでにEngineerからの線形化に含まれているクラスは省略されるので、線形化順序は、以下です。

Designer→Engineer→AnyRef→Any

 そして、この前にArchitectの線形化が行われます。ここでも、すでに線形化に含まれているクラスは省略されます。

Architect→Programmer→Designer→Engineer→AnyRef→Any

 最後に、線形化の先頭はPerson自身になります。

Person→Architect→Programmer→Designer→Engineer→AnyRef→Any

 これでPersonの線形化ができました。それぞれの線形化順序を表に示します。

線形化順序
Engineer Engineer→AnyRef→Any
Designer Designer→Engineer→AnyRef→Any
Programmer Programmer→Engineer→AnyRef→Any
Architect Architect→Programmer→Engineer→AnyRef→Any
Person Person→Architect→Programmer→Designer→Engineer→AnyRef→Any
表 サンプルで使用する型の線形化順序

 これらのクラス、トレイトがsuperを使用してメソッドを呼び出した場合に実行されるのは、線形化順序の自身の右隣にある実装です。このように、ルールに従って線形化され、superで何が呼び出されるかは明確に決まっています。

 なお、線形化の詳細な仕様についてはScalaの言語仕様書を参照してください。

次回は、型のパラメータ化

 今回はScalaの重要な機能であるトレイトを紹介しました。Javaのインターフェイスと似ている部分もありますが、実装を持つことができたり、複数トレイトのミックスインができたりと、幅広い使い方が可能です。

 次回は、型のパラメータ化を紹介します。

筆者紹介

クラスメソッド株式会社

中村修太(なかむら しゅうた)

クラスメソッド勤務の新しもの好きプログラマーです。昨年、東京から山口県に引っ越し、現在はノマドワーカーとして働いています。好きなJazzを聴きながらプログラミングするのが大好きです。



前のページへ 1|2|3       

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。