今回はJava SE 7で導入される機能について紹介していきます。
筆者が思うに、Java SE 7で最も議論を起こすであろう機能はクロージャです。クロージャの導入には紆余曲折がありましたが(詳しくは本記事の後半で述べます)、とうとう2009年11月に正式に導入されることが決まりました。
そこで、本連載ではいち早くクロージャについて取りあげることにします。
とは言うものの、現在提案されている仕様はまだたたき台レベルです。このため、今後仕様が大幅に変化する可能性があります。その点をご了承ください。
関数型とクロージャ
クロージャの仕様を紹介する前に、クロージャがどういうものかということを説明しましょう。
ここでは例として、ボタンをクリックしたときのイベント処理を考えてみます。
Swingではイベントの処理をリスナを用いて行います。例えば、以下のようなコードになります。
JButton button = ...;
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
// イベント処理
}
});
ボタンがクリックされると、リスナのactionPerformedメソッドがコールされます。
ここでは、actionPerformedメソッドを定義しているクラスを、ActionListnerインタフェースを実装した無名クラスとしています。
オブジェクト指向的には無名クラスのオブジェクトに対するメッセージパッシングですが、本当にクラスにする必要があるのでしょうか。
必要なのは、イベントが発生したときに行う処理であって、クラスは必須ではないと考えることもできます。
例えば、この処理をJavaFX Scriptで記述すると、次のようになります。
var button: Button = ...
button.action = function() {
// イベント処理
}
JavaFX Scriptではリスナ登録ではなく、Buttonクラスのactionプロパティに直接イベントを処理する関数を代入します。そう、オブジェクトではなく関数を直接記述できるのです。
つまり、actionプロパティの型は関数型になります。ここでは、無名関数を使用しましたが、無名でない関数を用いることもできます。
このように関数型を導入したことで、簡潔な記述になっています。
では、次にボタンをクリックしたら、カウンタが増加するというイベント処理を追加します。
単純にローカル変数countをactionPerformedメソッドでインクリメントしたいのですが、これはJavaではできません。
JButton button = ...;
int count = 0;
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
count++; // エラー
}
});
単にcountの値を参照するだけであれば、count変数をfinalとして定義することで実現できます。しかし、finalで定義すると、値を更新することはできません。
これを行うには、countをインスタンス変数として定義する必要があります。
private int count = 0;
public void init() {
...
JButton button = ...;
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
count++;
}
});
...
}
この処理をJavaFX Scriptで書いてみたらどうなるでしょう。
var button: Button = ...;
var count: Integer = 0;
button.action = function(): Void {
count++;
}
なんと、countがローカル変数であるのに無名関数の中から扱うことができるのです。このようなスコープの概念をレキシカルスコープと呼びます。
そして、レキシカルスコープを利用して、処理とその処理で利用する変数をセットにして扱う概念をクロージャといいます。
クロージャを使用することで、イベント処理の記述もずいぶんすっきりしました。
この例ではJavaFX Scriptを使用しましたが、JavaScriptなど多くのスクリプト言語でも関数型およびクロージャを使うことができます。
クロージャはコレクションやビジターバターンなどさまざまな用途に使えます。特にコレクションと一緒にクロージャを使うことが多く見受けられます。
しかし、これまでの説明では、クロージャを使えれば簡潔にかけるぐらいの利点しかありません。
無名クラスを使うことで関数型の代わりをさせることも可能です。レキシカルスコープは使えませんが、はじめからインスタンス変数を持たせるような設計にすれば特に問題になることはありません。
では、なぜJava SE 7に取り入れることになったのでしょう。次章では歴史的な経緯からクロージャ導入へ至る過程を追ってみます。