J2SE 5.0(Tiger)の新機能 言語編 Ariel Networks 開発部 中山 淳 J2SE 5.0(Tiger)の新機能 より単純に、より安全に • Generics • 拡張for文 • Autoboxing • Static import • 可変長引数 • Enumerations • Annotation Generics 型安全なプログラミング 今までのJava List list = new ArrayList(); list.add(new Integer(0)); Integer i0 = (Integer)list.get(0); // type-castが必要 list.add(new Long(0)); Integer i1 = (Integer)list.get(1); // ClassCastExceptionが発生 所々でtype-castが必要になるため、 型に関して安全とは言えない Tigerなら List<Integer> list = new ArrayList<Integer>(); // 新しい構文 list.add(new Integer(0)); Integer i = list.get(0); // type-castが不要 list.add(new Long(0)); // Integerしか入れられないので、compile error! compilerが型を知っているのでtype-castが不要になる compilerに型を教える新構文 List<Integer> list = new ArrayList<Integer>(); Genericsをサポートしているclass/interfaceに対して 「型名<型パラメータ>」の形で使う • ArrayList<Integer>は、Integer型の要素からなる ArrayList型 (class) • Map<String, Date>は、String型のkeyとDate型のvalueか らなるMap型 (interface) • Set<Map<String, Date>>は、String型のkeyとDate型の valueからなるMapのSet 自分でGenericなclass/interfaceを 書くこともできる 例えば、任意の型の変数の組を 保持するclass Pairを書いてみる 今までのJava class Pair { private Object first; private Object second; public Pair(Object first, Object second) { this.first = first; this.second = second; } public Object getFirst() {return first;} public Object getSecond() {return second;} } public static void main() { Pair pair = new Pair("now", new Date()); String key = (String)pair.getFirst() Date value = (Date)pair.getSecond() } Tiger class Pair<F, S> { private F first; private S second; public Pair(F first, S second) { this.first = first; this.second = second; } public F getFirst() {return first;} public S getSecond() {return second;} } public static void main() { Pair<String, Date> pair = new Pair<String, Date>("now", new Date()); String key = pair.getFirst() Date value = pair.getSecond() } Genericsで何が嬉しいか • 実行時エラー(ClassCastException)が減る • コードの見通しがよくなる • IDE(Eclipse等)による自動補完の精度が 上がる Genericsの原理 型パラメータはコンパイル時にのみ使われ、byte codeには残らない Pair<String, Date> pair = new Pair<String, Date>("now", new Date()); String key = pair.getFirst() Date value = pair.getSecond() は、次のコードと等価になるよう、コンパイルされる。 Pair pair = new Pair("now", new Date()); String key = (String)pair.getFirst() Date value = (Date)pair.getSecond() コンパイル時のtype-checkによって、type-castの正しさが 保証されるところがミソ。 既存のコードとの相互運用性 原理から分かるように、型パラメータを取るclass/interfaceは 型パラメータを取らない同名のclass/interfaceと互換性がある 既存のコード void foo(List list) {/* etc. */} void bar(Pair pair) {/* etc. */} Genericsを使ったコード void baz() { List<Integer> list = new ArrayList<Integer>(); foo(list); // 正しいコード (warningは出る) Pair<String, Date> pair = new Pair<String, Date>("now", new Date()); bar(pair); // Pair<F, S>はPair classとしてコンパイルされるので、これも可 } Genericsの制限 1. 型パラメータに組込型を指定できない List<int>と書くことはできない (List<Integer>は可) 2. 型パラメータで指定された型をnewできない (配列も含む) public S getSecond() {return new S();} や public S[] getSecond() {return new S[1];}は不可 3. byte codeになったときにmethodの型が同じになるようなmethodは記述で きない void set(F first) と void set(Object o) を同じclass/interfaceに書くのは不可 4. 型パラメータをstatic memberに適用できない static List<F> commons = new List<F>(); といった記述はできない 拡張for文 オブジェクトを列挙するforループを 同一の構文で書ける 今までのJava int[] intArray = new int[]{4004, 8008, 8080, 80, 6502, 6809}; for (int i = 0; i < intArray.length; i++) { int elem = intArray[i]; } List<Integer> intList = new ArrayList<Integer>(); // etc. for (Iterator i = intList.iterator(); i.hashNext(); ) { Integer elem = i.next(); } Tiger int[] intArray = new int[]{4004, 8008, 8080, 80, 6502, 6809}; for (int elem : intArray) { } List<Integer> intList = new ArrayList<Integer>(); // etc. for (Integer elem : intList) { } 拡張for文に渡せるコンテナ • 配列 • java.lang.Iterableをimplementsしたclassの オブジェクト (Java.util.Collection等) 拡張for文の実装 byte codeには、普通のfor文と同じようにコンパイルされる for (int elem : intArray) { } は、 for (int #i = 0; i < intArray.length; #i++) { int elem = intArray[#i]; } とコンパイルされる Iterableなものに関しても、同じようにiteratorを変数として コンパイルされる Autoboxing/unboxing int等の基本型とInteger等の wrapper型の自動変換を行い、 記述を単純にする Autoboxing/unboxingを使わないと void handleInt(int i); void handleInteger(Integer i); List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(1)); // Integer objectを生成 handleInteger(new Integer(1)); int i = list.get(0).intValue(); // Integer objectを取り出して、intValue() methodを呼び出す handleInt(list.get(0).intValue()); Autoboxing/unboxingを使うと void handleInt(int i); void handleInteger(Integer i); List<Integer> list = new ArrayList<Integer>(); list.add(1); // 自動的にInteger objectを生成 (boxing) handleInt(list.get(0)); // method呼び出しにも適用できる int i = list.get(0); // Integer objectを取り出して、自動的に // intValue() methodを呼び出す (unboxing) handleInteger(1); // method呼び出しにも適用できる Static import class/interfaceのstaticメンバを 直接importする 1.4までのJavaでは、import できるのはパッケージだけ class/interfaceのstaticメンバを参照するとき は、TypeName.MemberNameの形式で記述 する必要があった 今までのJava import java.util.Arrays; Arrays.sort(fooArray); Tiger import static java.util.Arrays.sort; sort(fooArray); // static importによってArraysを // 省略できる 定数interface idiomからの脱却 今までは、interfaceにstatic finalで定 数を書き、それをimplementsすること で定数を手軽に使えるようにする手 法がよく使われていた static importによって不要に 今までのJava package foo; public interface Barable { static final int BarConstant = 0; } public Bar implements foo.Barable { public static void main() { System.out.println(BarConstant); } } Tiger package foo; public class BarUtil { public static final int BarConstant = 0; } import static foo.BarUtil.*; public Bar { public static void main() { System.out.println(BarConstant); } } 可変長引数 printf()が使えるようになった 宣言 System.out.printf(String formt, Object ... args); 使用例 System.out.printf("%d = %s", 256, "0x100"); 可変長引数を取るmethodの例 void foo(Object ... vargs) { for (Object o : vargs) { System.out.println(o.toString()); } } static void main() { foo(1, "abc", new Date()); foo("abc", new Date(), "def", 89); } 可変長引数の実装 void foo(Object ... vargs) は、byte code上は void foo(Object[] vargs) と等価 → 1. foo(new Object[]{1, “abc”, new Date()}) と foo(1, “abc”, new Date())は同じbyte codeになる 2. void foo(Object ... vargs) と void foo(Object[] vargs) のoverloadingはできない 配列を可変長引数methodの 一引数として渡すには? 引数の型(foo()の場合はObject)にtype-castする foo("this array ", (Object)(new Object[]{1, "abc", new Date()}), "is one of parameters"); Enumerations C/C++でおなじみの列挙型 型安全な定数を手軽に使えるように なった (type safe enum idiomが不要に) 今までのJava package foo; public class DayOfWeek { static final int SUNDAY = 0; static final int MONDAY = 1; /* 略 */ static final int SATURDAY = 6; } void foo(int dayOfWeek) { switch (dayOfWeek) { case DayOfWeek.SUNDAY: /* 略 */ break; case DayOfWeek.MONDAY: /* 略 */ default: assert false; break; } } Tiger public enum DayOfWeek { SUNDAY = 0, MONDAY = 1, /* 略 */ SATURDAY = 6, } void bar(DayOfWeek dayOfWeek) { if (dayOfWeek == DayOfWeek.SUNDAY) { // 通常の記法 /* 略 */ } } void foo(DayOfWeek dayOfWeek) { switch (dayOfWeek) { case SUNDAY:// switch文の特例 /* 略 */ break; case SUNDAY: /* 略 */ default: assert false; break; } } enumの実装 定義 public enum DayOfWeek { SUNDAY, MONDAY, /* 略 */ , SATURDAY, } byte code public final class DayOfWeek extends java.lang.Enum { public static final DayOfWeek SUNDAY = new DayOfWeek(); public static final DayOfWeek MONDAY = new DayOfWeek(); /* 略 */ public static final DayOfWeek SATURDAY = new DayOfWeek(); public static final DayOfWeek[] values(); public static final DayOfWeek valueOf(String name); static { } } enum定数のinstanceは一つであることが保証されている (==演算子や!=演算子で比較できる) → そのためにいくつかの制限が発生 1. 2. 3. enum型にfinalやabstractといった宣言を行えない enum型のinstanceをnewで生成できない Reflection APIを使ってenum型のinstanceを 生成できない 便利なmethod String name() enum定数の名前を文字列で得る static T Enum.valueOf(String name) 文字列で渡された名前をenum定数に変換する static T[] Enum.values() enum定数を列挙する Annotation コードの動的な振る舞いに 影響しないmeta dataを付加する 標準のannotation • @Override methodに対して付与し、そのmethodがsuper classの methodをoverrideしていることを宣言する 現状の実装ではjavacが処理を行い、overrideされていな いケースで警告を出す 例) @Override public void foo(String name) { /* 略 */} • @Deprecated method/classに対して付与し、そのmethod/classが非推奨 であることを宣言する 現状の実装ではjavacが処理を行い、deprecatedされた methodが使用された箇所で警告を出す 例) @Deprecated public void bar(String name) { /* 略 */} 独自annotationの使用例 • Spring Framework 2.0 代表的なDI(Dependence Injection) コンテナ 2.0ではannotationでbeanを定義できる • JUnit 4 単体テストのフレームワーク テストメソッドの指定などにannotationを使用できる • Aspect J 1.5.x JavaでAOP(Aspect Oriented Programming)を行うための 言語 • 無設定Struts Annotationと命名規約によって、Strutsの定義ファイルを 不要にする J2SE1.4のJVMでTigerの機能を使うには 今までの説明から分かるとおり、Tigerの byte codeは1.4とほぼ同じ しかし、Tigerのclassファイルは1.4では実行 できない → WebSphere 6.0 (1.4ベース) で困る Tigerのclassファイルを1.4で実行できる形式 に変換するツールがある • Retroweaver • Retrotranslator ArielではRetrotranslatorを使って実際に運用 しているが、今まで問題が出たことはない
© Copyright 2024 ExpyDoc