Upload
eiichi-kimura
View
1.387
Download
0
Embed Size (px)
Citation preview
© 2012 EIICHI KIMURA. All Rights Reserved. 1
JJUG Cross Community Conference 2012 Fall
[R1-1] 今さら Coin,されど Coin (#jjug_r11)
2012/11/10 木村英一
2012/11/10 13:00-14:00
木村 英一 って、誰 ?
武蔵小杉駅 横須賀線口の前に立つビルで
Java 案件支援, チューニング 諸々に対応してます。
Twitter : @kimuchi583
URL : http://kimuchi583.at.webry.info/
http://blog.kimu2.jp/
© 2012 EIICHI KIMURA All Rights Reserved. 2 2012/11/9
Project Coin での主な改良ポイント
Switch/case ラベルに文字列リテラル
整数リテラルの拡張記法
1つのcatchで複数例外をキャッチ
ダイヤモンド(<>) 総称インスタンス生成時の型推論拡張
リソース対応 try 文 - 自動リソース管理
@SafeVarargs アノテーション導入
© 2012 EIICHI KIMURA. All Rights Reserved. 3 2012/11/10 13:00-14:00
Switch/case ラベルに文字列リテラル
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 4
String in Switch
case ラベルに文字列リテラル OK int monthNameToDays(String s, int year) {
} © 2012 EIICHI KIMURA. All Rights Reserved. 5
switch(s) { case "April": case "June": case "September": case "November": return 30; case "January": case "March": case "May": case "July": case "August": case "December": return 31; case "February": ... default ... }
if(s.equals("April") || s.equals("June") || s.equals("September") || s.equals("November")) return 30; if(s.equals("January") || s.equals("March") || s.equals("May") || s.equals("July") || s.equals(“August”) || s.equals("December")) return 31; if(s.equals("February")) ... else ... }
2012/11/10 13:00-14:00
switch/case 文の String 型対応 – ○ なところ
◙ “ switch(式)”の 式 に
String 型が結果となる式を記述できる
◙ case ラベルに “文字列リテラル”(式)を記述できる
◙ case ラベルに
String の定数(final)変数を記述できる
© 2012 EIICHI KIMURA. All Rights Reserved. 6 2012/11/10 13:00-14:00
○ case ラベルが文字列リテラル式・定数
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 7
public class StringInSwitch10 {
static final String ss = “s”;
public static void main(String[] args) {
String s = "Hello JDK7";
switch(s) {
case "Hello" + " " + "JDK7" : // 文字列リテラルの連結
System.out.println("Match case label :" +
"¥"Hello¥" + ¥" ¥" + ¥"JDK7¥"");
break;
case ss : // ss は static final 修飾された定数
System.out.println(“Match case lable : ss”);
break;
default :
System.out.println("Match default label");
break;
}
}
}
Switch/case 文の String 型対応 – × なところ
×式 に String[Buffer | Builder] 型は記述できない
×case ラベルに null を記述できない
×式 が null でも default ラベルに分岐しない
×異なる字面でも、同じ文字列を表す文字列リテラルは、同一 switch 文の case ラベルに記述できない
×case ラベルに String 変数は記述できない
× 大文字小文字を無視した比較はしない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 8
× String[Buffer | Builder] 型は記述できない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 9
public class StringInSwitch5 {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("s");
switch(sb) { // sb は StringBuffer なのでコンパイルエラーとなる
case "s" :
System.out.println("Match case label : ¥"s¥"");
break;
default :
System.out.println("Match default label");
break;
}
}
} StringInSwitch5.java:5: エラー: 互換性のない型 switch(sb) { // sb は StringBuffer なのでコンパイルエラーとなる
^
期待値: int 検出値: StringBuffer
× case ラベルに null を記述できない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 10
public class StringInSwitch1 {
public static void main(String[] args) {
String s = null;
switch(s) {
case null : // コンパイルエラーとなる System.out.println("Match case label : null");
break;
case "s" :
System.out.println("Match case label : ¥"s¥"");
break;
default :
System.out.println("Match default label");
break;
}
}
} StringInSwitch1.java:6: エラー: 定数の文字列式が必要です case null : // コンパイルエラーとなる
^
× 式が null の時 default ラベルに分岐しない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 11
public class StringInSwitch2 {
public static void main(String[] args) {
String s = null;
switch(s) { // s が null なので実行時エラー(NPE)発生 // case null :
// System.out.println("Match case label : null");
// break;
case "s" :
System.out.println("Match case label : ¥"s¥"");
break;
default :
System.out.println("Match default label");
break;
}
}
} コンパイルはできるが、実行時にエラーとなる。 Exception in thread "main" java.lang.NullPointerException
at StringInSwitch2.main(StringInSwitch2.java:5)
× 字面が異なる同じ文字列は記述できない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 12
public class StringInSwitch {
final static String ss = “¥u3046”; // “う” public static void main(String... args) {
switch(args[0]) {
case "あ“ : System.out.println("あ"); break;
case "い“ : System.out.println("い"); break;
case “う” : System.out.println(“う”); break; case "え" : System.out.println("え"); break;
case "お" : System.out.println("お"); break;
case ss : System.out.println(ss); break;
default : System.out.println("default"); break;
}
}
} >javac StringInSwitch.java StringInSwitch.java:11: エラー: caseラベルが重複しています case ss : System.out.println(ss); break;
^
× final でない変数は case に記述できない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 13
public class StringInSwitch3 {
static String ss = "s";
public static void main(String[] args) {
String s = "s";
switch(s) {
case ss : // ss は final でないので、コンパイルエラーとなる System.out.println("Match case label : ss");
break;
default :
System.out.println("Match default label");
break;
}
}
}
StringInSwitch3.java:8: エラー: 定数の文字列式が必要です case ss : // ss は final でないので、コンパイルエラーとなる
^
生成されるバイトコードは 2 つの switch 文に
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 14
switch(s) {
case "99" : System.out.println("case 99"); break;
case "98" : System.out.println("case 98"); break;
case "¥u0720" : System.out.println("case ¥u0720"); break;
}
String tmp_s = s;
int caselable = -1;
switch(tmp_s.hashCode()) {
case 1834 : // 1834 == "99".hashCode() == "¥u0720".hashCode()
if(tmp_s.equals(“¥u720”)) caselable = 2;
else if(tmp_s.equals("99")) caselable = 0;
break;
case 1833 : // 1537 == "98".hashCode()
if(tmp_s.equals("98")) caselable = 1;
break;
}
switch(caselable) {
case 0 : System.out.println(“case 99”); break;
case 1 : System.out.println(“case 98”); break;
case 2 : System.out.println("case ¥u0720"); break;
}
コンパイルして得られたバイトコード に対応するソースコードイメージ
1つ目の switch 文は、
hashCode 値で分岐する
2つ目の switch 文で使う
caselabel の値を求める hash値が同じ場合 if 文で文字列比較
2つ目の Switch 文は、
1つ目の switch 文で
求めた caselabel で
分岐する switch文
整数リテラルの拡張記法
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 15
Binary Integral Literal & Underscores
in Numeric Literals
整数リテラルの拡張記法
二進数表現記法 : プリフィックス 0b, 0B
桁区切り ‘_’ 記法 : ‘_’ は複数重ねてもよい。
1346704470 → 1_346_704_470
0x50451456 → 0x5045_1456 → 0x50_45_14_56
0b01010000010001010001010001010110 →
0b0101_0000_0100_0101_0001_0100_0101_0110
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 16
2進数リテラル/桁区切り文字 – ○ なところ
◙ 2進数表現の整数リテラルは、整数リテラルが記述できる場所で利用できる
◙ 桁区切り文字は、複数 ‘_’ 文字を重ねてもよい
◙ 桁区切り文字は、整数リテラル、浮動小数点リテラルで利用できる
© 2012 EIICHI KIMURA. All Rights Reserved. 17 2012/11/10 13:00-14:00
○ 2進数表現は整数リテラルの使える場所で
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 18
class BinaryLiteral3 {
public static void main(String args[]) {
// 2進数表現の整数リテラルをメソッド引数に利用した例
String s2 = Integer.toString(0b1111101000, 2);
String s8 = Integer.toString(0b1111101000, 8);
String s10 = Integer.toString(0b1111101000, 10);
String s16 = Integer.toString(0b1111101000, 16);
System.out.format(" 2進数表現 = %s¥n", s2);
System.out.format(" 8進数表現 = %s¥n", s8);
System.out.format("10進数表現 = %s¥n", s10);
System.out.format("16進数表現 = %s¥n", s16);
}
}
02進数表現 = 1111101000 08進数表現 = 1750 10進数表現 = 1000 16進数表現 = 3e8
○ 桁区切り文字は複数 ‘_’ 文字を重ねてよい
桁区切り文字 ‘_’ を重ねる文字数を変えてもよい。
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 19
class BinaryLiteral4 {
public static void main(String args[]) {
double d2 = Double.longBitsToDouble(
0B0100_0000_0000_1001_0010_0001_1111_1011 ¥¥
_0101_0100_0100_0100_0010_1101_0001_1000L);
double d16 = Double.longBitsToDouble(
0x4009___21FB__5444_2D18L);
System.out.format("d16 = %2$e¥n", d16);
}
}
d2 = 3.141593e+00, d16 = 3.141593e+00
注) 表示の都合上の改行
○ ‘_’は浮動小数点数リテラルにも使える
© 2012 EIICHI KIMURA. All Rights Reserved. 20
class BinaryLiteral5 {
public static void main(String args[]) {
double minVd = 0x0.0__0000__0000__0001P-1_022;
// 2^(-1074) = Double.MIN_VALUE
double pI = 3.141592653589793; // Math.PI
float maxVf = 3.4028__2346__6385__2886E38f;
// Float.MAX_VALUE
String minVds = Double.toString(minVd);
String pIs = Double.toString(pI);
String maxVfs = Float.toString(maxVf);
System.out.format("min_value = %s¥n", minVds);
System.out.format("pi = %s¥n", pIs);
System.out.format("max_value = %s¥n", maxVfs);
}
} min_value = 4.9E-324
pi = 3.141592653589793
max_value = 3.4028235E38
0x0.1 = 16-1= 2-4
0x0.01 = 16-2=2-8
…
2012/11/10 13:00-14:00
2進数リテラル/桁区切り文字 - × なところ
×桁区切り文字を数値の先頭及び末尾には記述できない
×桁区切り文字を、浮動小数点数リテラルの指数部開始文字(e, p等)前後に記述できない
×文字列表現から数値変換するメソッド(例えばInteger#parseInt(String)など)の String 型の引数中に桁区切り文字を含んだ数値表現は記述できない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 21
× 桁区切り文字を数値の先頭/末尾におけない
© 2012 EIICHI KIMURA. All Rights Reserved. 22
class BinaryLiteral6 {
public static void main(String args[]) {
// リテラルの先頭(接頭子がある場合は、その直後)に置いてみた
int d2s = 0b_11_0000_0011_1001;
int d8s = 0_30_071; // これは OK !!
int d10s = _12_345; // これは変数扱い !!
int d16s = 0x_3039;
// リテラルの末尾に '_' を置いてみた
int d2t = 0b11_0000_0011_1001_;
int d8t = 030_071_;
int d10t = 12_345_;
int d16t = 0x3039_;
}
} BinaryLiteral6.java:4: エラー: 不正なアンダースコアです int d2s = 0b_11_0000_0011_1001;
^
…
2012/11/10 13:00-14:00
× ‘_’を指数部開始文字前後におけない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 23
class BinaryLiteral5 {
public static void main(String args[]) {
double minVd = 0x0.0__0000__0000__0001_P-1_022;
// 2^(-1074) = Double.MIN_VALUE, p の直前に '_' を置く
double pI = 3.1415_9265__3589___793; // Math.PI
float maxVf = 3.4028__2346__6385__2886E_38f;
// Float.MAX_VALUE, e の直後に ‘_’ を置く
String minVds = Double.toString(minVd);
String pIs = Double.toString(pI);
String maxVfs = Float.toString(maxVf);
System.out.format("min_value = %s¥n", minVds);
System.out.format("pi = %s¥n", pIs);
System.out.format("max_value = %s¥n", maxVfs);
}
} BinaryLiteral5.java:3: エラー: 不正なアンダースコアです
double minVd = 0x0.0__0000__0000__0001_P-1_022 ;
^
BinaryLiteral5.java:6: エラー: 不正なアンダースコアです
float maxVf = 3.4028__2346__6385__2886E_38f;
^
× 桁区切り文字は文字列数値変換で使えない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 24
class BinaryLiteral7 { public static void main(String args[]) { int i = Integer.parseInt("1_000"); System.out.format("i = %d¥n", i); } }
コンパイルはできますが、実行時にエラーとなります。 Exception in thread "main“ ¥
java.lang.NumberFormatException: For input string: "1_000“
at java.lang.NumberFormatException.forInputString( ¥
NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:492)
at java.lang.Integer.parseInt(Integer.java:527)
at BinaryLiteral7.main(BinaryLiteral7.java:3)
1 つの catch で複数例外をキャッチ
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 25
Multi-catch & More Precise Rethrow
1 つの catch で複数例外をキャッチ try {
// リフレクション操作 : 例えば Class.forName, Class.newInstance,
// Class.getMethod, Method.invoke などの呼出し処理
}
catch(ClassNotFoundException cnfe) {
log(cnfe);
throw cnfe;
}
catch(InstantiationException ie) {
log(ie);
throw ie;
}
catch(NoSuchMethodException nsme) {
log(nsme);
throw nsme;
}
catch(InvocationTargetException ite) {
log(ite);
throw ite;
}
© 2012 EIICHI KIMURA. All Rights Reserved. 26 2012/11/10 13:00-14:00
1 つの catch で複数例外をキャッチ
try {
// リフレクション操作 : 例えば Class.forName, Class.newInstance,
// Class.getMethod, Method.invoke などの呼出し処理
} catch(ClassNotFoundException | InstantiationException |
NoSuchMethodException | InvocationTargetException e) {
log(e);
throw e;
}
© 2012 EIICHI KIMURA. All Rights Reserved. 27 2012/11/10 13:00-14:00
try {
// リフレクション操作 : 例えば Class.forName, Class.newInstance,
// Class.getMethod, Method.invoke などの呼出し処理
}
catch(Exception e) {
log(e);
throw e;
}
X 従来のサボリ方 ... 当然、推奨しません !!
複数例外 catch と高精度再スロー - ○ なところ
◙ 複数の例外を 1 つの catch 節に ‘|’ で
区切って記述できる
◙ ReflectiveOperationException が 利用できる
◙ 再スローする例外のクラスを throws 句の例外宣言に記述できる
© 2012 EIICHI KIMURA. All Rights Reserved. 28 2012/11/10 13:00-14:00
○ 再スローする例外を throws に書ける
© 2012 EIICHI KIMURA. All Rights Reserved. 29
import java.io.*;
class ExceptionRethrow8 {
private static String encode = "S_JIS";
public static void main(String args[]) {
ExceptionRethrow8 er = new ExceptionRethrow8();
try { er.test(); }
catch(FileNotFoundException | UnsupportedEncodingException e) { System.out.println("@ main multi catch : "); e.printStackTrace();
};
}
void test() throws FileNotFoundException, UnsupportedEncodingException { InputStreamReader isr = null;
try {
// Data.txt が存在しない場合 FileNotFoundException が発生する
// encodeで指定された文字セットが未サポートの場合 UnsupportedEncodingException が発生する
isr = new InputStreamReader(new FileInputStream("Data.txt"), encode);
} catch(Exception e) { System.out.println("Exception @ test - catch : "); e.printStackTrace();
throw e; } finally {
try { if(isr != null) isr.close(); }
catch(IOException e) { System.out.println("Exception @ test - finally: " + e); }
}
}
} Exception で catch し再スローする時、 JDK6 では throws に Exception と記述する必要がある(さもないとコンパイル時エラー)
JDK7 では 実際に throw される例外(または、その親クラス)を throws に書ける。
2012/11/10 13:00-14:00
try {
// リフレクション操作 : 例えば Class.forName, Class.newInstance,
// Class.getMethod, Method.invoke などの呼出し処理
} catch(ClassNotFoundException | InstantiationException |
NoSuchMethodException | InvocationTargetException e) {
log(e);
throw e;
}
○ ReflectiveOperationException が使える
© 2012 EIICHI KIMURA. All Rights Reserved. 30 2012/11/10 13:00-14:00
JDK7 では ReflectiveOperationException が導入された。 ClassNotFoundException, IllegalAccessException, InstantiationException,
InvocationTargetException, NoSuchFieldException, NoSuchMethodException
の親クラス
try {
// リフレクション操作 : 例えば Class.forName, Class.newInstance,
// Class.getMethod, Method.invoke などの呼出し処理
} catch(ReflectiveOperationException e) {
log(e);
throw e;
}
○ ReflectiveOperationException が使える
© 2012 EIICHI KIMURA. All Rights Reserved. 31 2012/11/10 13:00-14:00
JDK7 では ReflectiveOperationException が導入された。 ClassNotFoundException, IllegalAccessException, InstantiationException,
InvocationTargetException, NoSuchFieldException, NoSuchMethodException
の親クラス
複数例外catchと高精度再スロー – × なところ
×複数の例外を ‘|’ で区切って記述する場合、例外パラメータへの代入はできない
×複数の例外を ‘|’ で区切って記述する場合、親子関係のある例外クラスを両方記述することはできない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 32
× 例外パラメータに代入できない
© 2012 EIICHI KIMURA. All Rights Reserved. 33
void test() throws IOException {
InputStreamReader isr = null;
try {
// Data.txt が存在しない場合 FileNotFoundException が発生する // encode で指定された文字セットがサポートされていない場合 // UnsupportedEncodingException が発生する
isr = new InputStreamReader(new FileInputStream("Data.txt"), encode);
} catch(FileNotFoundException | UnsupportedEncodingException e) {
System.out.println("Exception @ test - catch : "); e.printStackTrace();
e = new IOException(e); // コンパイルエラー throw e;
} finally {
try { if(isr != null) isr.close(); }
catch(IOException e) {
System.out.println("Exception @ test - finally: " + e);
}
}
}
複数の例外をキャッチする catch 節の例外パラメータは暗黙に final !! つまり、そのパラメータに代入できない !!
ExceptionRethrow5.java:19:エラー: 複数catchパラメータeに値を代入することはできません e = new IOException(e); // コンパイル時にエラーとなる ^
2012/11/10 13:00-14:00
× 親子関係のある例外を指定できない
© 2012 EIICHI KIMURA. All Rights Reserved. 34
void test() throws IOException {
InputStreamReader isr = null;
try {
// Data.txt が存在しない場合 FileNotFoundException が発生する // encode で指定された文字セットがサポートされていない場合 // UnsupportedEncodingException が発生する
isr = new InputStreamReader(new FileInputStream("Data.txt"), encode);
} catch(
FileNotFoundException | UnsupportedEncodingException | IOException e) {
System.out.println("Exception @ test - catch : "); e.printStackTrace();
throw e;
} finally {
try { if(isr != null) isr.close(); }
catch(IOException e) {
System.out.println("Exception @ test - finally: " + e);
}
}
}
親子関係にある例外クラスを、 複数の例外をキャッチする catch 節に指定するとコンパイルエラーとなる。
ExceptionRethrow10.java:16:エラー:
複数catch文の代替をサブクラス化によって関連付けることはできません } catch(FileNotFoundException | UnsupportedEncodingException | IOException e) {
^
代替FileNotFoundExceptionは代替IOExceptionのサブクラスです
2012/11/10 13:00-14:00
例外テーブルで catch 節の実行を制御(1) (生成するバイトコード命令列は同じ)
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 35
① catch(FileNotFoundException | UnsupportedEncodingException e) // Multi-Catch
void test() throws java.io.IOException; Code: 0: aconst_null … Exception table: from to target type 22 30 33 Class java/io/IOException 2 22 41 Class java/io/FileNotFoundException 2 22 41 Class java/io/UnsupportedEncodingException 2 22 48 any 49 57 60 Class java/io/IOException 41 49 48 any
② catch(IOException e) // Single-Catch w/親クラス - JDK6以前でも有効なコードの場合(1)
void test() throws java.io.IOException; Code: 0: aconst_null … Exception table: from to target type 22 30 33 Class java/io/IOException 2 22 41 Class java/io/IOException 2 22 48 any 49 57 60 Class java/io/IOException 41 49 48 any
バイトコード命令列は同じ
バイトコード命令列は同じ
例外テーブル に違いあり
例外テーブル に違いあり
例外テーブルで catch 節の実行を制御(2) (生成するバイトコード命令列は同じ)
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 36
③ catch(FileNotFoundException e) { } // Single-Catchを2つ ③ catch(UnsupportedEncodingException e) { } // JDK6以前でも有効なコードの場合(2)
void test() throws java.io.IOException; Code: 0: aconst_null … Exception table: from to target type 22 30 33 Class java/io/IOException 2 22 41 Class java/io/FileNotFoundException 2 22 48 Class java/io/UnsupportedEncodingException 2 22 55 any 49 57 67 Class java/io/IOException 41 49 55 any
バイトコード命令列も膨らむ
例外テーブル に違いあり
総称インスタンス生成時の型推論拡張
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved.
Pag
e 37
Diamond ~ Improved Type Inference for
Generic Instance Creation
総称型クラスのインスタンス生成
List list = new ArrayList();
List<String> list = new ArrayList<String>();
List<List<List<List<List<String>>>>> list
= new ArrayList<List<List<List<List<String>>>>>();
型引数宣言を2度書くのは面倒だ !!
List<String> list = new ArrayList<>();
List<List<List<List<List<String>>>>> list
= new ArrayList<>();
© 2012 EIICHI KIMURA. All Rights Reserved. 38
総称型を使うと
ダイヤモンドを使って、楽しよう
2012/11/10 13:00-14:00
ダイヤモンド記法 – ○ なところ
◙ ジェネリッククラスに対するクラス・インスタンス生成式(new式)で型引数の記述が省略で
きる
© 2012 EIICHI KIMURA. All Rights Reserved. 39 2012/11/10 13:00-14:00
○ new式で型引数を省略できる
© 2012 EIICHI KIMURA. All Rights Reserved. 40
package java.util;
public abstract class AbstractList<E>
extends AbstractCollection<E> implements List<E> {
// ...
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<>(this, fromIndex, toIndex) :
new SubList<>(this, fromIndex, toIndex));
}
// ...
} JDK 7 の java.util.AbstractList クラスの subList(int, int)メソッドのソースで、すでに利用している。
public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<E>(this, fromIndex, toIndex) :
new SubList<E>(this, fromIndex, toIndex));
}
JDK6 u23 のソースでは <E> になっている。
2012/11/10 13:00-14:00
ダイヤモンド記法 – ▲ なところ
▲代入式での変数の型として宣言された型と、コンパイラによる型推論の結果が同じになるとは限らない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 41
型推論の結果が直感的でないことも
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 42
public class DiamondTest<T> {
private T value;
public DiamondTest() { }
public DiamondTest(T value) { this.value = value; }
public T getValue() { return value; }
public static void main(String[] args) {
DiamondTest<Number> b1 = new DiamondTest<>();
DiamondTest<Number> b2 = new DiamondTest<>(1);
System.out.format(
"b1=%1$d, b2=%2$d¥n", b1.getValue(), b2.getValue()); }
} > DiamondTest.java
DiamondTest.java:9: エラー: 互換性のない型
DiamondTest<Number> b2 = new DiamondTest<>(1);
^
期待値: DiamondTest<Number>
検出値: DiamondTest<Integer>
ダイヤモンド記法 – X なところ
×クラス・インスタンス生成式(new式)でのみ利用可能であり、任意の箇所で型引数を省略することはできない
×New式の型引数が指定され、ジェネリッククラスの型引数が省略されるとコンパイルできない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 43
X new式以外では型引数を省略できない
© 2012 EIICHI KIMURA. All Rights Reserved. 44
import java.util.*;
public class DiamondTest7 {
public static void main(String[] args) {
List<List<String>> lls = new ArrayList<>(3);
ArrayList<String> als = new ArrayList<>();
als.add("1"); als.add("22"); als.add("333"); als.add("4444"); als.add("55555");
// new 式であるが lls.add(new ArrayList<Object>()) と推論するのでコンパイルエラー
// lls.add(new ArrayList<>());
lls.add(new ArrayList<String>());
lls.add(new ArrayList<String>());
lls.add(als);
// コンパイルエラー, (ArrayList<String>) でキャストすると OK
ArrayList<String> ls = (ArrayList<>)(lls.get(2));
String s = ls.get(4);
System.out.println("s = " + s); // s == "55555"
}
} DiamondTest7.java:15:エラー: 式の開始が不正です
ArrayList ls = (ArrayList<>)(lls.get(2));
^
2012/11/10 13:00-14:00
型引数の明示的指定時と同じバイトコード
“ダイヤモンド” と ”型引数を指定した” 場合と “Raw Type” の場合で、 生成されるバイトコードに違いなし。 ※部分が次のどの場合も、同じバイトコードが生成される。
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 45
import java.util.*;
class DiamondTest1 {
public static void main(String[] args) {
for(String s : args) {
arg_list.add(s);
}
System.out.println("arg_list = " + arg_list);
}
}
※ ArrayList[______] arg_list = new ArrayList[_____](args.length);
ArrayList<String> arg_list = new ArrayList< >(args.length);
ArrayList<String> arg_list = new ArrayList<String>(args.length);
ArrayList arg_list = new ArrayList (args.length);
自動リソース管理
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 46
try-with-resource statement
try-with-resources statement
(自動リソース管理) やっと、リソース解放を明示しなくてもよくなった
finally 節に close 処理を書くことが、守れていない ! 知らないし ...
さらに、例外発生個所によっては、その記述も面倒だった !!
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 47
InputStream in = new FileInputStream(src);
try {
OutputStream out = new FileOutputStream(dest) ;
try {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n); // ここで例外が発生し、 } finally {
out.close(); // ここでも例外が発生し、 }
} finally {
in.close(); // さらに、ここでも例外が発生するかも }
try 節の拡張記法 + AutoCloseable の導入
try 節の拡張記法
super interface java.lang.AutoCloseable の導入
finally での close 処理の記述が必要なくなる
例外発生中での別の例外発生時の処理の記述も必要なし
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 48
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
}
close 処理以外は catch/finally 節に記述する
close処理の記述は必要なくなるが、
例外発生の事実は変わらない !
catch 節や throws 句は必須 !!
© 2012 EIICHI KIMURA. All Rights Reserved. 49
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
// ...
} catch(IOExcption e) {
/* 例外発生時の処理を記述する */
}
public void exec() throws IOException {
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
// ...
}
}
OR
2012/11/10 13:00-14:00
リソース対応 try 文 – ○ なところ
◙ try-catch-finally のどのブロックにも、明示的な null チェックと、close() 呼出しとを記述しなくてよい
◙ 一つのリソース対応 try 文で、複数のリソースを宣言できる
◙ 例外ハンドラ内での例外発生時にも適切に処理される
© 2012 EIICHI KIMURA. All Rights Reserved. 50 2012/11/10 13:00-14:00
ちなみに、コンパイルすると ...(1)
© 2012 EIICHI KIMURA. All Rights Reserved. 51
try(InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dest)) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} catch(IOExcption e) {
/* 例外発生時の処理を記述する */
}
try {
InputStream in = new FileInputStream(src);
Throwable pe1 = null;
try {
OutputStream out = new FileOutputStream(dest);
Throwable pe2 = null;
In, out の null チェックのコードと、
close の呼出しは書かないでよい
複数のリソースを宣言できる
コンパイルして得られたバイトコード に対応するソースコードイメージ
2012/11/10 13:00-14:00
ちなみに、コンパイルすると ...(2)
© 2012 EIICHI KIMURA. All Rights Reserved. 52
try {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) >= 0)
out.write(buf, 0, n);
} catch(final Throwable e1) {
pe2 = e1; throw e1;
} finally {
if(out != null) {
if(pe2 != null) {
try { out.close(); }
catch(Throwable e2) { pe2.addSuppressed(e2); }
} else {
out.close();
}
}
}
out の close 処理を finally で実行。
out の null チェックと、
close の際に例外発生した場合、
先に発生した例外の中に取り込む。
そして、上位にスローされるのはあくまでも最初の例外である。
2012/11/10 13:00-14:00
ちなみに、コンパイルすると ...(3)
© 2012 EIICHI KIMURA. All Rights Reserved. 53
} catch(final Throwable e3) {
pe1 = e3;
throw e3;
} finally {
if(in != null) {
if(pe1 != null) {
try { in.close(); }
catch(Throwable e4) { pe1.addSuppressed(e4); }
} else {
in.close();
}
}
}
} catch(IOException e) {
/* 例外発生時の処理を記述する */
}
In の close 処理を finally で実行。
in の null チェックと、
close の際に例外発生した場合、
先に発生した例外の中に取り込む。
そして、上位にスローされるのはあくまでも最初の例外である。
2012/11/10 13:00-14:00
addSuppressed()された例外のスタックトレース Exception @ main :
java.lang.RuntimeException : java.io.IOException
at TryWithResource8.test(TryWithResource8.java:39)
at TryWithResource8.main(TryWithResource8.java:20)
Caused by: java.io.IOException
at TryWithResource8.test(TryWithResource8.java:35)
... 1 more
Suppressed: java.lang.AssertionError
at TryWithResource8_resource.close(TryWithResource8.java:11)
at TryWithResource8.test(TryWithResource8.java:36)
... 1 more
© 2012 EIICHI KIMURA. All Rights Reserved. 54
①
②
③
catch(IOException ex) { // ②
Throwable pex = ex;
pex.addSuppressed(new AssertionError()); // ③
throw new RuntimeException(ex); // ①
}
2012/11/10 13:00-14:00
リソース対応 try 文 – X なところ
×catch, finally ブロックでは明に close() 呼出しできない
×close() 以外に必要な後始末処理は省略できない
×リソース対応 try 文の宣言した変数に値を代入できない
×AutoClosable ではないクラスはリソース宣言できない
×try ブロック内で宣言したリソースはclose()を呼出さない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 55
X catch/finally からリソース変数にアクセス不可
© 2012 EIICHI KIMURA. All Rights Reserved. 56
import java.io.*;
import java.sql.*;
import javax.sql.*;
class TryWithResource6 {
void test() { int n = 1; // プレースホルダーインデックス String str = "column1"; // プレースホルダ置換え文字列
try (Connection con = DriverManager.getConnection(url, usr, pwd);
PreparedStatement ps = con.prepareStatement(“…")) {
con.setAutoCommit(false);
ps.setString(n, str); // n 番目のプレースホルダに str を指定
int res = ps.executeUpdaet();
// ...
con.commit();
} catch (SQLException ex) {
if(con != null) // con はスコープ外なのでコンパイル時のエラーとなる con.rollbak(); // con はスコープ外なのでコンパイル時のエラーとなる ex.printStackTrace();
}
}
}
このような場合は、通常の try-catch-finally を使う
2012/11/10 13:00-14:00
X リソース宣言した変数に代入できない
© 2012 EIICHI KIMURA. All Rights Reserved. 57
import java.io.*; class TryWithResource5 { void test() { String str = null; try(BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream("Data.txt"), "SJIS"))) { while((str = br.readLine()) != null) System.out.println(str); br = new BufferedReader( new InputStreamReader( new FileInputStream(“AnotherData.txt”, “SJIS”)); } } }
TryWithResource8.java:15: エラー :
自動クローズ可能なリソースbrに値を代入することはできません
br = new BufferedReader(new InputStreamReader( ¥
new FileInputStream("AnotherData.txt"), “SJIS”));
^
Try-with-resource 文で宣言されたリソースの変数は final となる。
2012/11/10 13:00-14:00
X AutoClosable 未実装はリソース宣言できない
© 2012 EIICHI KIMURA. All Rights Reserved. 58
import java.io.*;
import java.awt.*;
class TryWithResource7 {
void test() {
try (SplashScreen sps = SplashScreen.getSplashScreen()) {
// ...
}
}
}
TryWithResource7.java:6: エラー: try-with-resourceは変数型に適用されません
try (SplashScreen sps = SplashScreen.getSplashScreen()) {
^
期待値: AutoCloseable
検出値: SplashScreen
close メソッドがあるクラスのインスタンスなら何でもよい… わけではない。 AutoCloseable インタフェースの実装クラスのインスタンスでなければならない。
2012/11/10 13:00-14:00
X リソース対応 try ブロック内の宣言は対象外
© 2012 EIICHI KIMURA. All Rights Reserved. 59
public class TryWithResource9 {
public static void main(String args[]) {
TryWithResource9 er = new TryWithResource9();
er.test(); }
void test() {
Res9_res r11 = null;
Res9_res r12 = null;
try (Res9_res r1 = new Res9_res ("[Resource_1]");) {
Res9_res r2 = new Res9_res ("[Resource_2]");
r11 = r1; // finally で確認できるようにスコープの広い変数に代入
r12 = r2; // finally で確認できるようにスコープの広い変数に代入
} finally {
System.out.println();
// スコープの広い変数(r11, r12)で状態を確認する
System.out.println(r11.getName() + " is " +
(r11.isClosed() ? " CLOSED" : "NOT CLOSED !!"));
System.out.println(r12.getName() + " is " +
(r12.isClosed() ? " CLOSED" : "NOT CLOSED !!"));
}
}
}
2012/11/10 13:00-14:00
X リソース対応 try ブロック内の宣言は対象外
© 2012 EIICHI KIMURA. All Rights Reserved. 60
class Res9_res implements AutoCloseable {
String name = "[null]";
boolean closed_flag = false;
public Res9_res (String str) { name = str; };
public void close() {
closed_flag = true;
System.out.println(name + " is closed NOW !!");
}
public boolean isClosed() { return closed_flag; }
public String getName() { return name; }
}
[Resource_1] is closed NOW !! // r1 の close が呼び出された
// r2 の close は呼び出されない
[Resource_1] is CLOSED // r1 は close されている
[Resource_2] is NOT CLOSED !! // r2 は close されていない
2012/11/10 13:00-14:00
@SafeVarargs アノテーション導入
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 61
Simplified Varargs Method Invocation
Varargs メソッド呼出しの簡素化
@SafeVarargs アノテーションの導入 次の 2 つの警告を抑制する
可変引数メソッド自身の宣言に関する無検査警告
可変引数メソッドのメソッド起動式で無検査警告
コアライブラリで利用 - 無意味な警告を抑制する JDK7 の次のメソッドで @SafeVarargs が利用されている
public static <T> List<T> java.util.Arrays.asList(T... a)
public static <T> boolean java.util.Collections.addAll(Collection<? super T> c, T... elements)
public static <E extends Enum<E>> java.util.EnumSet<E> EnumSet.of(E first, E... rest)
protected final void javax.swing.SwingWorker.publish(V... chunks)
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 62
Varargs メソッド呼出しの簡素化
ジェネリッククラスのインスタンスを可変引数として渡されるメソッド、コンストラクタに対して 呼び出す側(利用者)
無意味なエラーが出力されない
呼び出される側(ライブラリ開発者)
利用者に余分な警告メッセージを提示しないようにできる
より複雑な型での可変引数メソッドに効果あり
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 63
// Sting の List - JDK6 でも警告メッセージは出力されない List<String> ls = Arrays.asList(
"", "1", "22", "333", "4444",
"55555", "666666", "7777777", "88888888", "999999999“);
// String の List の List - JDK6では警告メッセージが出力されていた, JDK7 では警告なし List<List<String>> lls = Arrays.asList(
Arrays.asList("", "1", "22", "333", "4444"),
Arrays.asList("55555", "666666", "7777777", "88888888", "999999999")
);
Varargs メソッド呼出しの簡素化 – ○ なところ
◙ 可変引数メソッドを利用する立場では、 余分な警告メッセージを気にかけずにすむ
◙ ライブラリ開発者の立場では、 余分な警告メッセージに気をつかわせることなく利用してもらえる
© 2012 EIICHI KIMURA. All Rights Reserved. 64 2012/11/10 13:00-14:00
○ ジェネリック配列の Varargs の無検査警告
© 2012 EIICHI KIMURA. All Rights Reserved. 65
import java.util.*;
class SafeVarargsTest1 {
public static void main(String args[]) {
List<String> l1 = new ArrayList<String>();
Collections.addAll(
l1, "", "1", "22", "333", "4444", "55555", "666666", "7777777", "88888888", "999999999");
List<String> l2 = new ArrayList<String>();
List<String> l3 = new ArrayList<String>();
copyAll(l1, l2, l3); System.out.println(“l1 = ” + l1); ...
}
// @SafeVarargs
static void copyAll(List<String> from, List<String>... tos) {
// from → tos[0]...tos[n] に要素をコピーする
for(List<String> ls : tos) {
ls.addAll(from);
}
}
} > javac -Xlint:unchecked SafeVarargsTest1.java
SafeVarargsTest1.java:9: 警告:
[unchecked] 型List<String>[]の可変引数パラメータに対する総称型配列の無検査作成です
copyAll(l1, l2, l3);
^
SafeVarargsTest1.java:16: 警告:
[unchecked] パラメータ化された可変引数型List<String>からのヒープ汚染の可能性があります
static void copyAll(List<String> from, List<String>... tos) {
^ JDK7 でのコンパイル結果(@SafeVarargs がない場合)
利用側
ライブラリ提供側 : JDK7 でコメントをはずすと2つの警告が抑制される
JDK6 でのコンパイル結果
2012/11/10 13:00-14:00
Varargs メソッド呼出しの簡素化 – X なところ
×@SafeVarargs アノテーションは、
× メソッド/コンストラクタ以外には使えない
× 固定引数のメソッド/コンストラクタには使えない
× static でも final でもない可変引数メソッドには使えない
×メソッド/コンストラクタ本体内のコードに対する警告を抑制しない
2012/11/10 13:00-14:00 © 2012 EIICHI KIMURA. All Rights Reserved. 66
X 固定引数メソッドの @SafeVarargs はエラー
© 2012 EIICHI KIMURA. All Rights Reserved. 67
import java.util.*;
class SafeVarargsTest4 {
public static void main(String args[]) {
List<String> l1 = new ArrayList<String>();
Collections.addAll(l1, "", "1", "22", "333", "4444", "55555");
List<String> l2 = new ArrayList<String>();
listcopy(l1, l2); System.out.println("l1 = " + l1);
System.out.println("l2 = " + l2);
}
@SafeVarargs
static void listCopy(List<String> from, List<String> tos) {
for(String s : from) {
tos.add(s);
}
}
} SafeVarargsTest4.java:14: エラー: SafeVarargs 注釈が無効です。
メソッドlistCopy(List<String>,List<String>)は可変引数メソッドではありません。
static void listCopy(List<String> from, List<String> tos) {
^
2012/11/10 13:00-14:00
X @SafeVarargs はメソッド警告を抑止しない
© 2012 EIICHI KIMURA. All Rights Reserved. 68
@SafeVarargs // @SuppressWarnings(“unchecked”)を指定すると警告は出ず、実行時エラーの可能性あり
static void copyAll(List<String> from, List<String>... tos) {
List[] lists = tos;
lists[0].add(new StringBuilder("A")); // 引数のデータを無理やり変更 lists[0].add(new StringBuilder("B")); // 引数のデータを無理やり変更
for(List<String> ls : tos) {
for(String s : from) {
ls.add(s);
}
}
}
SafeVarargsTest8.java:23: 警告:
[unchecked] raw 型 List のメンバーとしての add(E) への無検査呼出しです
lists[0].add(new StringBuilder("A")); // 引数のデータを無理やり変更
^
E が型変数の場合: インタフェース List で宣言されている E extends Object
SafeVarargsTest8.java:24: 警告:
[unchecked] raw 型 List のメンバーとしての add(E) への無検査呼出しです
lists[0].add(new StringBuilder("B")); // 引数のデータを無理やり変更
^
E が型変数の場合: インタフェース List で宣言されている E extends Object
2012/11/10 13:00-14:00
まとめ
各々 できること・できないことがある
Switch/case ラベルに文字列リテラル
整数リテラルの拡張記法
1つのcatchで複数例外をキャッチ
ダイヤモンド(<>)
リソース対応 try 文
@SafeVarargs アノテーション導入
実行時エラーとなるケースは要注意 !!
© 2012 EIICHI KIMURA. All Rights Reserved. 69 2012/11/10 13:00-14:00