こんにちは。
Java7から導入されたtry-with-resources。イイですよね、便利ですネ簡素にかけますネ。
しかも、Java9になると、さらにスマートな書き方が導入されました。
だ...だけど、ちょっとハマるというか、残念なことがあったので、記事にしてみました。
まずは、Java7とJava8のtry-with-resourcesの書き方
書籍「Effective Java -Third Edition-(Joshua Bloch著)」の36ページに掲載されてあるfirstLineOfFileメソッドを、ここに掲載します。mainメソッドは私のオリジナルです。
public class Java7and8TryWithResources {
public static void main(String[] args) {
String val = firstLineOfFile("test.txt", "-------");
System.out.println(val);
}
static String firstLineOfFile(String path, String defaultVal) {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
}
この書籍、Java9についても対応したよ!と謳い文句にあるのですが、try-with-resourcesに関しては上記のサンプルコードを掲載したどまりでおしまい。ちょっと残念。
Java9から書けるようになった新try-with-resources構文
Java9から書けるようになった新try-with-resources構文では、変数宣言はtry()の中ではなく、try構文の前で書けるようになりました。
public class Java9NewTryWithResources {
public static void main(String[] args) {
String val = "-------";
try {
val = firstLineOfFile("test.txt", val);
} catch (FileNotFoundException e) {
// 略
}
System.out.println(val);
}
static String firstLineOfFile(String path, String defaultVal) throws FileNotFoundException {
BufferedReader br = new BufferedReader(new FileReader(path)); // ここ注目!😲
try (br) { // ここも注目!😲
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
}
さて、この新構文ですが、オフィシャルな出典を探そうと試みました。
More concise try-with-resources statements in JDK 9
ブログなのですが、ここはURLがoracle.comなので、一応公式なのかな、と。
【1日遅れで追記】オフィシャルな出典を見つけました。Java Language Changes for Java SE 9です。でも、すぐ上のブログと同じことしか書いてないや。
https://docs.oracle.com/javase/9/ や https://docs.oracle.com/javase/10/ も見たんですが、そこにリンク張ってある「Java Tutorials」が現在、
The Java Tutorials have been written for JDK 8. Examples and practices described in this page don't take advantage of improvements introduced in later releases.
(JavaチュートリアルはJDK 8向けに書かれています。このページで説明されている例とプラクティスは、それ以降のリリースで導入された改良点を利用していません。)
とあり、ズッコケ🤷です。そのうちJDK 9向けのチュートリアルが掲載されることを祈ります。
公式ではないところのサイトだと、
- http://javasampleapproach.com/java/java-9/java-9-try-resources-improvement
- https://www.sejuku.net/blog/41943
が見つかりました。
嬉しいことと、残念だと思うこと
上掲のJava9NewTryWithResources.javaとJava7and8TryWithResources.javaとを比較してみます。
嬉しいこと
BufferedReader br = new BufferedReader(new FileReader(path));
try (br) {
残念なところ
FileNotFoundExceptionをスローするnew FileReader(path)
のせいで、以下の2点が残念に思います。
static String firstLineOfFile(String path, String defaultVal) throws FileNotFoundException {
String val = "-------";
try {
val = firstLineOfFile("test.txt", val);
} catch (FileNotFoundException e) {
// 略
}
System.out.println(val);
try-with-resourcesは何が為に?
あなたと呼べば あなたと答える
みなさんご存知の名曲、ディック・ミネと星玲子の『二人は若い』の冒頭ですね。懐かしい。作詞はさすがサトウハチロー先生です。
それになぞらえて、私はこう歌ってしまいます。
「try」と呼べば「例外」と答える。
ところが!です。「try-with-resources」は、ただの「try」ではないのです。AutoClosable
が為にあるのです。そこを失念してはいけません!
くだんのnew FileReader(path)
にイチャモンを付けましたけど、だってこれ、try { … } の中に書いてないもん!BufferedReader
をオートクローズしてもらいたくてtry-with-resources書いたんだもん!
こんな書き方もできる
でも、FileReader
もAutoClosable
なんです。なので、以下のようにも書けます。
public class Java9NewTryWithResources2 {
public static void main(String[] args) {
String val = "-------";
try {
val = firstLineOfFile("test.txt", val);
} catch (FileNotFoundException e) {
// 略
}
System.out.println(val);
}
static String firstLineOfFile(String path, String defaultVal) throws FileNotFoundException {
FileReader fr = new FileReader(path);
BufferedReader br = new BufferedReader(fr);
try (fr; br) { // 複数並べて書けます
return br.readLine();
} catch (IOException e) {
return defaultVal;
}
}
}
これでもfirstLineOfFileメソッドにthrows FileNotFoundException
を付けざるをえないんですけどね。つまり、
コンストラクタ/メソッド | 例外 | どうする? |
---|---|---|
BufferedReader のコンストラクタ |
例外をスローしない | try-with-resourcesでオートクローズしてもらうだけでOK |
FileReader のコンストラクタ |
FileNotFoundException をスローする |
メソッドのシグネチャにthrows付ける |
BufferedReader のreadLine メソッド |
IOException をスローする |
catchする |
この三者三様に気づかないと、「え?なんでbrは好くて、frはダメなの?」とか、「何が為のIOExceptionをキャッチ?しかもFileNotFoundExceptionってIOExceptionのサブクラスだし...」などとヤキモキしてしまうことになります。
まとめ
Java9の新try-with-resources構文、好いんだか悪いんだか。悩みます。まとめになってなくて、すみません。🙇
以上です。