自分型アノテーションとは、自身のインスタンスを表すための「this」キーワードの別名のようなものです。基本コンストラクタの先頭で定義できます。名前は何を付けてもいいのですが、慣習的には「self」を使うことが多いようです。
class Sample { self => val msg = "hello" def show = { println("this.msg = " + this.msg);println("self.msg = " + self.msg) } }
scala> new Sample().show this.msg = hello self.msg = hello
当然、thisキーワードと自分で付けた別名は同じものを示します。これだけでは、あまり活用方法がありませんが、実は自分型アノテーションでは定義時に自身以外の別の型(クラス/トレイト)を指定可能です。
こうすることで、そのクラス/トレイトを自身が継承していることと同じ扱いになります(複数のトレイトをミックスインしたい場合、「self: A with B with C =>」とします)。
それでは、自分型アノテーションを使ってコンポーネント間の依存関係を定義してみましょう。まずはMyServiceトレイトを定義し、そのトレイトを実装するMyServiceImplトレイトを実装します。
trait MyService { def findAll():String }
trait MyServiceImpl extends MyService { def findAll():String = "MyServiceImpl#findAll" }
次に、自分型アノテーションでMyServiceを指定したMyControllerクラスを定義します。
class MyController { self: MyService => def execute = { println(findAll()) } }
MyController内でMyServiceのメソッドを使えるのが分かります。では、MyControllerをインスタン化して使ってみましょう。MyServiceの実装がまだ決まっていないので、インスタン化の際にMyServiceImplを指定しています。
scala> val c = new MyController with MyServiceImpl c: MyController with MyServiceImpl = $anon$1@69dfe453 scala> c.execute MyServiceImpl#findAll
MyControllerを実行すると、インスタン化時にミックスインしたMyServiceImplのfindAllメソッドが実行されています。ミックスインするクラスを変更すれば、簡単に実装を切り替えができます。
このように、自分型アノテーションを使うことで、DI(依存性の注入)機能をソースファイルだけで実現できます。
さらに、この手法だとコンパイル時にエラーが発見できるというメリットもあります。
ただ、extendsして継承した場合は、「super」キーワードで継承したメソッドを呼び出せますが、自分型アノテーションでは呼び出せないので注意してください。
2012年8月現在、Scalaのバージョンは2.9.2がstableとなっており、次期バージョン2.10はMilestone 6となっています。ここでは、2.10で導入が検討されている機能をいくつか紹介します。
これは、暗黙の型変換をより直感的に記述する手法です。Scalaでは暗黙の型変換を使い、継承を用いずに型を拡張できます。
まず、いままでの手法を見てみましょう。例として、Int型に暗黙の型変換を使ってshowメソッドを追加してみます。
//Intを拡張したクラス class RichInt(self: Int) { def show(): Unit = println(self) } //IntからRichIntへ暗黙の型変換を行う implicit def toRichInt(self: Int): RichInt = new RichInt(self)
scala> 100.show 100
ここでは、以下の2ステップを踏んでおり、少々面倒です。
しかし、2.10から導入される予定のimplicit Classを使うことで、もう少し簡潔に記述できます。implicit Classを使うには、拡張した結果のクラスに「implicit」キーワード付与し、そのクラスのコンストラクタに、拡張元のクラスを指定します。
//Intを拡張したクラス(Scala 2.10を使う必要があります) implicit class RichInt(self: Int) { def show(): Unit = println(self) }
scala> 100.show 100
いままではJavaのリフレクションAPIを使うしかありませんでしたが、2.10からはScalaのリフレクションAPIが導入されます。
このAPIを利用すると、Scala固有のリフレクション情報(implicitなど)を取得できます。リフレクションに関する情報は、こちらを参考にしてみてください。
2.10では、まだ実験的な導入ですが、この機能を用いるとコンパイル時に値が評価され、マクロを呼んでいる部分にインラインで展開されるようになります。
すでに、いくつかのライブラリでは実用例もあるようです。マクロに関しては下記を参考にしてください。
前回の記事でも少し書きましたが、いままで使っていたScalaのアクターは非推奨になり、Akka(Java/Scala用並行処理ライブラリ)のアクターが導入されます。
現状でも並行処理を記述する際はAkkaを使うことが多いと思いますが、2.10からはScala標準のアクターとして使えるようになります。
これ以外にもいくつか新しい機能が実装される予定です。2.10の正式リリースはもう少し先になるとは思いますが、公式サイトからダウンロードできるので、新機能に興味がある方は試してみてください。
さて、8カ月前から始まったこの連載も今回で最終回です。全11回にわたってScalaの基本を紹介してきましたが、いかがでしたか?
連載第1回にScalaの紹介として、「オブジェクト指向、関数型言語の機能を統合することでより簡潔にコードの記述ができ、生産性が向上する」と言いました。いままでの連載を振り返ると、Scalaのいろいろな機能が“簡潔で分かりやすいコードを記述するため”にできていることが分かると思います。
この数カ月でScalaを採用した実例はさらに増えました。国内でも事例が出てきていることから分かるように、すでにScalaは実用的な言語として認識されつつあります。
Scalaは今後、まだまだ進化していく言語です。当然昔のバージョンのサポートはされていくとは思いますが、新しい機能の追加や変更もされていくでしょう。
以前よりScalaの情報は入手しやすくなっており、日本語の書籍も何冊か出版されているので、最新の情報を追うことはそんなに難しくはないと思います。
ここまで本連載をご覧いただき、ありがとうございました。この連載をきっかけに、Scalaに触れていただけたら幸いです。
中村修太(なかむら しゅうた)
クラスメソッド勤務の新しもの好きプログラマです。昨年、東京から山口県に引っ越し、現在はノマドワーカーとして働いています。好きなJazzを聴きながらプログラミングするのが大好きです。
Copyright © ITmedia, Inc. All Rights Reserved.