拡張されたforループの利用
これまでのJavaには、for eachループ構文がありませんでした。Visual BasicやC#などの言語にはこの構文があるので、開発者はコレクションの要素に対するループ処理を容易に行うことができます。for eachループを用いると、反復子を使わずにコレクション内部の要素を参照できます。J2SE 5.0になって(ようやく)Javaにも、従来のforループを拡張する形でfor eachループ構文が用意されました。
この拡張されたforループは、J2SE 5.0の機能のなかでも最も期待されていたものの1つです。この機能を使えば、さきほど紹介したコードのIteratorがすっかり不要になるため、さらに簡潔なコードが得られます。拡張されたforループを使ってJ2SE 5.0用に変更したコードを以下に示します。
import java.util.*; public class EnhancedForCollection { public static void main(String args[]) { ArrayList<String> list = new ArrayList<String>(); list.add( new String("One") ); list.add( new String("Two") ); list.add( new String("Three") ); //list.add( new StringBuffer() ); for( String str : list ) { System.out.println( str ); } } }
このコードでは、元のコードからIteratorとwhileループが完全に削除され、次の3行になっています。
for( String str : list ) {
System.out.println( str );
}
拡張されたforループの書式は次のとおりです。
for( [collection_item_type] [item_access_variable] :
[collection_to_be_iterated] )
最初のパラメータであるcollection_item_type(コレクション要素の型)は、コレクション作成時に指定したジェネリック型に対応するJavaの型でなければなりません。ここで扱うコレクションは前述のArrayListなので、コレクション要素の型はStringになります。
2番目のパラメータitem_access_variableは、アクセス変数の名前です。この変数は、コレクションに対するループ処理を繰り返す際に各コレクション要素の値を入れるためのものです。このアクセス変数の型もまた、collection_item_typeで指定したものと同じでなければなりません。
3番目のパラメータcollection_to_be_iteratedは、繰り返し処理の対象となるコレクションを示します。この変数は、コレクション型として宣言されたものであり、かつcollection_item_typeと一致するジェネリック型を持っていなければなりません。この2つの条件を満たしていない場合は、コンパイルエラーが発生します。
プリミティブデータ型とオートボクシング
J2SE 5.0に追加されたもう1つの重要な機能が、プリミティブデータ型のボクシングとアンボクシングを自動的に行う機能です。コレクションAPIでプリミティブデータ型の追加と参照を行うコードは複雑になりがちですが、このオートボクシング機能により、コードをかなり簡略化できます。
ボクシングとアンボクシングについて馴染みがない方は、J2SE 5.0について知る前に、Javaがプリミティブデータ型をどのように扱うかをまず知っておく必要があります。ここでは、本題のJ2SE 5.0に入る前に、コレクションAPIでプリミティブデータ型を利用する簡単なJavaアプリケーションを紹介しましょう。
import java.util.*; public class PrimitiveCollection { public static void main(String args[]) { ArrayList list = new ArrayList(); // box up each integer as they are added list.add( new Integer(1) ); list.add( new Integer(2) ); list.add( new Integer(3) ); //list.add( new StringBuffer() ); // now iterate over the collection and unbox Iterator itr = list.iterator(); while( itr.hasNext() ) { Integer iObj = (Integer)itr.next(); int iPrimitive = iObj.intValue(); System.out.println( iPrimitive ); } } }
上記のコードは、コレクションAPIでプリミティブ型を用いるために必要な2つの手順を示しています。コレクション型は、プリミティブ型ではなくオブジェクト型を格納するため、プリミティブ型の要素を格納したいときには、適切なラッパーオブジェクトにボクシングしなければなりません。たとえば、上記のコードでは、次のようにしてintプリミティブデータ型をIntegerオブジェクトにボクシングしています。
list.add( new Integer(1) ); list.add( new Integer(2) ); list.add( new Integer(3) );
しかし、このボクシングを行うと、コードの後の部分で、コレクションに格納されたプリミティブ型のint型変数にアクセスしようとするときに問題が起こります。プリミティブデータ型は、Integerオブジェクトとして格納されたため、コレクションから返されるときも、int型の変数ではなくIntegerオブジェクトとして返されます。そのため、ボクシングのときと逆の変換(アンボクシング)が必要になります。
Iterator itr = list.iterator(); while( itr.hasNext() ) { Integer iObj = (Integer)itr.next(); int iPrimitive = iObj.intValue(); System.out.println( iPrimitive ); }
この例の場合は、まずは各要素をIntegerオブジェクトとして取り出し、iObjとします。次に、intValueメソッドを呼び出すことで、このIntegerオブジェクトをプリミティブ型であるint型に変換します。
J2SE 5.0のオートボクシングを使えば、コレクションでのプリミティブ型の格納と取り出しがとても容易になります。次に例を示します。
import java.util.*; public class AutoBoxCollection { public static void main(String args[]) { ArrayList<Integer> list = new ArrayList<Integer>(); // box up each integer as it's added list.add( 1 ); list.add( 2 ); list.add( 3 ); //list.add( new StringBuffer() ); // now iterate over the collection for( int iPrimitive: list ) { System.out.println( iPrimitive ); } } }
このオートボクシングのサンプルでは、最初にIntegerのジェネリック型を持つArrayListを作成しています。
ArrayList<Integer> list = new ArrayList<Integer>();
Integerというラッパー型をジェネリック型とするリストを作成した後は、プリミティブ型であるint型変数を直接このリストに追加できます。
list.add( 1 ); list.add( 2 ); list.add( 3 );
この場合、それぞれの整数をIntegerオブジェクトでラップする必要はもうありません。さらに、リスト内にある個々の要素の参照もまた容易になります。
for( int iPrimitive: list ) { System.out.println( iPrimitive ); }
この例は、拡張されたforループを使ってプリミティブデータ型のコレクションの反復処理を行う方法を示しています。ここでは、型変換の必要はなく、プリミティブ型の各変数をint型として直接取り出すことができます。つまり、オートボクシングとアンボクシングによって、プリミティブデータ型をオブジェクトと同じくらい容易にコレクションで扱うことができるのです。
まとめ
J2SE 5.0にジェネリック型が追加されたことで、コレクションに格納するデータの型を明確に指定できるようになりました。これによって、コレクションが正しい型のオブジェクトだけを受けとることがコンパイラによって保証され、コレクションで扱われる要素の型変換が不要になります。
また、ジェネリック型により、拡張されたforループの利用が可能になりました。この拡張されたforループは他のプログラミング言語に見られるfor each構文と同じ機能を提供しています。拡張されたforループ構文では、反復子が不要なため、コレクションの各要素に対する繰り返し処理の記述がかなり簡潔になります。
これまで、コレクションへのプリミティブデータ型の追加は、常に面倒なものでした。int型のようなプリミティブ型をいったんIntegerオブジェクトにボクシングした後、再びint型に戻すためにアンボクシングしなければならなかったのです。オートボクシングとアンボクシングによって、この処理を開発者ではなくJava側に負担させることができ、その結果、プリミティブデータ型を持つコレクションを利用する際のソースコードはずっと読みやすいものになります。
こうした変更によって、J2SE 5.0のコレクションデータへのアクセス方法は必然的に変化し、コレクションAPIにアクセスするためのJavaコードもずっとシンプルなものになっています。
