JDK5で新しく盛り込まれたいくつかの機能を使ってみます。
使用したのはEclipse3.1M2です。Eclipseでは3.1M1からJDK5に対応(一部の機能のみ)しています。
この機能こそ、JDK5で最も重要な機能と言えるでしょう。
具体的には、クラスに「型付け」を行います。
C++をやっていた人なら、テンプレート機能と説明した方がわかりやすいかもしれません。
java.util.List
インターフェイスは従来、
あらゆるオブジェクトを格納できる作りになっていました。
しかし、実際にはこのような作りにすることは少なく、
何らかのクラスに特定したリストにすることが多かったはずです。
簡単な例を挙げてみます。
List list = new ArrayList(); list.add(new Integer(1)); list.add(new Integer(2)); ... int size = list.size(); for (int i = 0 ; i < size ; i++) { Integer value = (Integer)list.get(i); ... }
getメソッドの戻り値は Object
なので、
Integer型として取り出すにはキャストをする必要があります。
この例で言えば、Integer型以外のオブジェクトがリストに格納されることは
想定されていません。
それなのに毎回キャストを行うのは面倒です。
しかも、Javaにとってキャスト処理というのは重い処理なので性能的に言っても良くありません。
これを解消するのがGenericsです。
List<Integer> list = new ArrayList<Integer>(); ... for (int i = 0 ; i < size ; i++) { Integer value = list.get(i); ... }
宣言時に <Integer>
というキーワードを付けています。
こうすることによって、このリストはInteger型のみを格納することが保証されます。
getメソッドはInteger型を返すので、キャストの必要はありません。
例えば <String>
というキーワードを使えば、getメソッドはString型を返します。
このように、宣言に応じてメソッドの戻り値や引数の型を動的に変更できるのが
Generics の特徴です。
Listインターフェイスの例でいえば、getメソッドだけでなく
add / iterator などのメソッドも引数や戻り値の型が変わります。
なお、一番目に示したプログラムは、宣言に <Object>
というキーワードを付けた場合と
全く同じ動作をします。そのため、JDK1.4以前のソースもそのまま使うことが出来ます。
これによって、リストに間違った型のオブジェクトが入る可能性を
未然に防ぐことが出来ます。
上の例で言えば、Integer型以外のオブジェクトをaddメソッドの引数に指定すると
「コンパイルエラー」が発生します。
従来のやり方の場合、リストに間違った型のオブジェクトを格納しても
その時点では判断が付かずに取得時のキャストで例外が発生していました。
それに比べれば、実行前の段階で間違いが判るというのは相当なメリットになるのです。
新しいループの構文です。
これを使うと、先程の例は以下のように書き換えられます。
for (Integer value : list) { ... }
インデックスやイテレータの為に変数を使わなくて済むので、
プログラムはとてもスマートになります。
しかも、listはリスト型の他に配列型でもOKなので
Integer[] list = new Integer[size];
のように宣言文を変更した場合でも、ループの構文はそのまま使うことが出来ます。
以前までのJavaでは、配列とリストを使い分ける必要がありました。
これは、リストが明確な型を持っていなかったからです。
プログラムの可読性を上げる為に、リストから配列への変換をする場面もよく目にしたはずです。
しかし、Genericsの導入によって
リストは配列と同じだけの可読性を持つようになりました。
これにより、配列を使うメリットは限り無く少なくなったと言えます。
強いて挙げるならば、
・メモリ使用量が少ない
・定数などで使う場合には宣言が簡単なため便利
くらいでしょうか。ほとんどの場面で、可変長のリストを使った方が
プログラムが組みやすくなります。
ListなどのJava標準リストクラスは全てEnhanced for loopに対応しています。
自分で作ったクラスのオブジェクトを使ってこのループ構文を使うには、java.util.Iterable
インターフェイスを実装すればOKです。
class SampleClass implements Iterable { private List list; public Iterator iterator() { return list.iterator(); } }
Genericsを使う場合には、例えば以下のようにします。
class SampleClass<E> implements Iterable<E> { private List<E> list; public Iterator<E> iterator() { return list.iterator(); } }
JDK1.5では、大量のクラス・パッケージが追加されています。
ここでは少しずつそれらを紹介していきます。
StringBuffer
とほぼ同じですが、こちらは同期化がされていません。
よって、マルチスレッド環境では使えませんが
シングルスレッド下では StringBuffer
以上のパフォーマンスを期待できるでしょう。