J2SE 5.0(Tiger)の新機能 言語編

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を使って実際に運用
しているが、今まで問題が出たことはない