J2EE関連の最新トピックをわかりやすく解説

J2EE Watch [6]


J2SE 5.0「Tiger」で何が変わるか?


テクニカルライター
吉川和巳
2004/10/16



 サン・マイクロシステムズが「Java 2 Platform Standard Edition(J2SE)5.0」、いわゆる「Tiger」を9月30日にリリースしてからおよそ2週間が過ぎた。さっそく今後のプロジェクトで導入すべきかどうか迷い始めている読者も少なくないだろう。

 1995年のJava言語登場以来の言語仕様の変更は、サン・マイクロシステムズが推し進める“EoD(Ease of Develpment)”の一貫だ。プログラミングの簡素化の方向性は、この先EJB3.0やJSF(JavaServer Faces)を取り込んだJ2EE 5.0の登場に引き継がれていく(J2EE 5.0は2005年後半に登場予定)。

 そこで本稿では、EoDを指向する新しいJava言語のコアとなるJ2SE 5.0について、現場のITエンジニアにとってどのような点がありがたいのかという観点から、新機能を概観してみたい。

「ジェネリック型」でコレクションがタイプセーフに

 「ジェネリック型」のサポートを心待ちにしていたJavaプログラマはとても多いはずだ。ジェネリック型とは、Javaコード上の「型名」をパラメータ化し、コンパイル時に任意の型名で置き換えるメカニズムである。ジェネリック型を使えば、特定の型に依存しないコードを書きつつ、コンパイラによる型チェックのメリットを享受できる。

 Javaのコレクションの例を用いてそのメリットを説明しよう。Javaプログラマーであれば、コレクションを扱う以下のようなコードを年中書いているだろう。

for (Iterator it = list.iterator(); it.hasNext();) {
    String msg = (String) it.next();
    ...
}

 ご存じのとおり、コレクションにはあらゆるオブジェクトを格納できる。なぜならコレクションに要素を追加するaddメソッドの引数や、Iteratorからオブジェクトを取り出すnextメソッドの戻り値が、いずれも「Object型」として定義されているからだ。

 このようにObject型を使えば、メソッドの引数や戻り値としてあらゆる型を扱うことができる。その半面デメリットも多く、例えば上記の「(String) it.next();」というコードのように、随所にキャストを記述しなくてはならない。またコレクションに出し入れするオブジェクトの型をコンパイラでチェックできないので、実行時にClassCastExceptionが発生して初めてバグに気付くことになる。「厳密な型チェック」というJavaの長所が失われてしまうのだ。

 こうした問題を解決するのがジェネリック型である。J2SE 5.0では以下のようなコードが書けるようになった。

//Listに要素を追加
List<String> list = new LinkedList<String>();
list.add("Hello,");
list.add("World!");

//Listの内容を表示
for (Iterator<String> it = list.iterator(); it.hasNext();) {
    String msg = it.next();
    System.out.println(msg);
}

 ここで、ListやLinkedList、Iteratorといったコレクションのクラス名やインターフェイス名に続いて<String>と記されていることに注目していただきたい。J2SE 5.0から導入されたこの新しい記法を用いることで、従来はObject型として定義されていた引数や戻り値の型が、コンパイル時にString型で置き換えられる。つまり「String型専用のコレクション」となるのだ。よって仮に「list.add(new Integer(123));」と書けばコンパイルエラーが発生する。また「String msg = it.next();」というコード例のとおり取得したオブジェクトのキャストが不要で、実行時にClassCastExceptionが発生する恐れもない。

 <String>の部分には任意の型名を指定できる。例えば<String>を<Integer>に置き換えれば、今度はInteger型専用のコレクションとなる。つまり「あらゆる型に対してタイプセーフなコレクション」なのだ(ただしintなどプリミティブ型は指定できない)。

 ジェネリック型の応用範囲はコレクションにとどまらない。Javaプログラマーがクラスやインターフェイスを作成する際にジェネリック型を使えば、特定の型に依存しない抽象的なアルゴリズムやライブラリを記述できる。これまでC++やJGLの専売であった「ジェネリック・プログラミング」の世界がJavaプログラマーにも開かれるのである。

新しい「forループ」でイテレートがとても簡単に

 またJ2SE 5.0では「forループ」が改良され、コレクションを逐次処理するループをとても簡単に記述できるようになった。この新しいforループを使えば、上記コード例のforループ部分は以下のように記述できる。

    for (String msg : list) {
        System.out.println(msg);
    }

 これはつまり、「コレクションからIteratorを取得する」「Iteratorに要素が残っているか調べる」「Iteratorから次の要素を取得する」という一連の処理のコーディングをすべて省略できるということだ。

プリミティブ型を自動変換する「オートボクシング」

 J2SE 5.0の新機能「オートボクシング」も、やはりコレクションの使いやすさの向上に大きく貢献している。オートボクシングは、intやdoubleなどのプリミティブ型とIntegerやDoubleなどのラッパクラス間の相互変換を自動化する機能だ。例えば以下のようなコードを書けるようになる。

List list = new LinkedList();
list.add(123);

 従来、2行目の部分は「list.add(new Integer(123));」と記述していた。コレクションはプリミティブ型のデータを格納できないので、Integerオブジェクトでラップしなくてはならないからだ。一方J2SE 5.0では、上記のようなコード部分で必要に応じてオートボクシングが働くため、ラッパクラスに変換するコードを書く手間が掛からない。またその逆に、ラッパクラスからプリミティブ型への変換(アンボクシング)も自動化されている。これらの機能はコレクション以外の場面でも何かと重宝するだろう。

マルチスレッドの七つ道具「java.util.concurrentパッケージ」

 Javaの特徴はマルチスレッドとの親和性だ。しかし標準装備されているsynchronizedやwait/nofityメソッドは簡単なスレッド同期を行うだけであり、より複雑なスレッド制御はすべて自作するしかない。例えばリードライト・ロックで読み込み処理を高速化したい、セマフォで一定数のリソースを共有したい、スレッド・プールと非同期処理でスケーラビリティを高めたい……と思ってはみても、その敷居の高さに尻込みしてしまうケースは少なくないだろう。

 こうしたマルチスレッドの扱いにくさを緩和するのが「java.util.concurrentパッケージ」だ。同パッケージは、Javaマルチスレッド・プログラミングの教科書「Concurrent Programming in Java」の著者Doug Leaを中心に開発された、高レベルのスレッド・ライブラリである。例えばセマフォは以下のようなコードで利用できる。

final Semaphore s = new Semaphore(1, true); 
s.acquire(); //セマフォを取得
try {
    balance = balance + 10; //リソースにアクセス
} finally {
    s.release(); //セマフォを解放
}

 そのほかにも、リードライト・ロックをはじめ、スレッド・セーフで高パフォーマンスなキュー、スレッド・プールと非同期処理のためのフレームワーク、またスレッド・セーフかつロック不要な変数をプロセッサ・レベルで実装したatomicパッケージなどが提供されている。

「メタデータ」でアノテーションが可能に

 J2SE 5.0では新たなシンタックスとして「メタデータ」がサポートされた。これはJavaコード上にさまざまなアノテーション(注釈)を記述するための仕組みだ。このアノテーションを使えば、Javaコードだけでは伝え切れない付加的な情報をコンパイラや開発ツールに伝達できる。コンパイラによるコードのチェックをはじめ、コードやドキュメント自動生成、DI(Dependency Injection)などに応用が可能だ。

 最も分かりやすい例が@Overrideアノテーションである。Javaプログラマーであれば、メソッド・シグネチャのちょっとした違いでオーバーライドに失敗し、コンパイラでチェックできないバグを生み出した経験があるだろう。J2SE 5.0ではそうした悩みも解決される。

@Override
public String toString() {
    ...
}

 このように@Overrideアノテーションを記述しておけば、確実にオーバーライドされていることをコンパイラ・レベルでチェックしてくれる。

 メタデータが威力を発揮するのは、EJBやWebサービス、JDBCなど、サーバサイド・プログラミングの分野だ。ミドルウェアの多彩な機能とJavaコードを結び付ける手段としてアノテーションを活用できる。例えば分散オブジェクトやXML RPCを通じてリモート・アクセスしたいメソッドがあれば、@remoteアノテーションで以下のように指定すればよい。従来のようにRemoteインターフェイスをimplementsしたりDDファイルを用意したりする手間は不要だ。

public @remote void helloWorld() {
    ....
}


その他の新機能

 ここで取り上げたのはJ2SE 5.0の新機能の一部にすぎない。これらのほかにも、Javaプログラマにとって見逃せないトピックがいくつかある。最後にそのいくつかを駆け足で見ていこう。

標準入出力のフォーマット

 C言語のprintf風のフォーマット機能。手軽な文字列の整形が可能だ。

System.out.printf("%s %5d%n", user, total);

可変長引数のサポート

 メソッド呼び出し時の可変長引数のサポート。例えば以下のように、引数の型に続いて「...」と記述すれば、任意数の引数を受け取って配列としてアクセスできる。

public void test(String ... msgs) {
    for (int i = 0; i < msgs.length; i++) {
        System.out.println(msgs[i]);
    }
}

JDBC RowSet実装クラスの追加

 RowSetはResultSetと同等のデータベース・カーソル機能を持つJavaBeansコンポーネント。J2SE 5.0では新たに5つの実装クラスが追加された。その1つであるCachedRowSetは、データベースから切り離して使えるオフラインのRowSetで、後でデータベースと同期を取ることができる。またWebRowSetは、データソースとしてデータベースではなくXML文書を利用できるRowSetである。

Unicode補助文字をサポート

 Javaのchar型は16ビット・データであり、Unicodeの補助文字(コードポイントU+10000〜U+10FFFF)を表現できない。そこでJ2SE 5.0ではchar型の代わりにint型で補助文字のコードポイントを扱うメソッドがStringクラスやCharacterクラスに追加された。例えばisLowerCase/isUpperCaseその他多数のメソッドにint型引数を取るものが用意されている。またString中の特定位置からint型のコードポイントを取得するcodePointAtメソッドが実装された。

 以上、本稿ではJ2SE 5.0における主な新機能をざっと紹介した。これらのシンタックスやライブラリが実際のプロジェクトで使えるようになる日が待ち遠しいところだ。




Java Solution全記事一覧

 



Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間