Java9の新try-with-resources

こんにちは。

Java7から導入されたtry-with-resources。イイですよね、便利ですネ簡素にかけますネ。
しかも、Java9になると、さらにスマートな書き方が導入されました。
だ...だけど、ちょっとハマるというか、残念なことがあったので、記事にしてみました。

# まずは、Java7とJava8のtry-with-resourcesの書き方

書籍「[Effective Java -Third Edition-（Joshua Bloch著）](https://www.pearson.com/us/higher-education/program/Bloch-Effective-Java-3rd-Edition/PGM1763855.html)」の36ページに掲載されてあるfirstLineOfFileメソッドを、ここに掲載します。mainメソッドは私のオリジナルです。

```java:Java7と8のtry-with-resources
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構文の前で書けるようになりました。**

```java:Java9の新try-with-resources構文
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](https://blogs.oracle.com/darcy/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/language/toc.htm#JSLAN-GUID-B06D7006-D9F4-42F8-AD21-BF861747EDCF)です。でも、すぐ上のブログと同じことしか書いてないや。

https://docs.oracle.com/javase/9/ や https://docs.oracle.com/javase/10/ も見たんですが、そこにリンク張ってある「[Java Tutorials](https://docs.oracle.com/javase/tutorial/tutorialLearningPaths.html)」が現在、

> 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向けのチュートリアルが掲載されることを祈ります。:milky_way:

公式ではないところのサイトだと、

- http://javasampleapproach.com/java/java-9/java-9-try-resources-improvement
- https://www.sejuku.net/blog/41943

が見つかりました。

# 嬉しいことと、残念だと思うこと

上掲のJava9NewTryWithResources.javaとJava7and8TryWithResources.javaとを比較してみます。

## 嬉しいこと

うわっ…私のtry()の中、短すぎ…？！　![shock_woman.png](https://qiita-image-store.s3.amazonaws.com/0/100612/868bd5d1-a697-0bce-9f2d-c2d5e3bdaf0d.png)



```java:たったこれだけで、イイの？！イイんです。クゥ～！👍
BufferedReader br = new BufferedReader(new FileReader(path));
try (br) {
```

## 残念なところ

FileNotFoundExceptionをスローする`new FileReader(path)`のせいで、以下の2点が残念に思います。

```java:throws句が付いちゃったよ😧
static String firstLineOfFile(String path, String defaultVal) throws FileNotFoundException {
```

```java:mainの中が行数増えたよ😩
String val = "-------";
try {
    val = firstLineOfFile("test.txt", val);
} catch (FileNotFoundException e) {
     // 略
}
System.out.println(val);
```

# try-with-resourcesは何が<ruby>為<rt>ため</rt></ruby>に？

:musical_note:*あなたと呼べば あなたと答える*:notes:

みなさんご存知の名曲、ディック・ミネと星玲子の『二人は若い』の冒頭ですね。懐かしい。作詞はさすがサトウハチロー先生です。

それになぞらえて、私はこう歌ってしまいます。

:musical_note:*「try」と呼べば「例外」と答える。*:notes:

ところが！です。「try-with-resources」は、ただの「try」ではないのです。`AutoClosable`が為にあるのです。そこを失念してはいけません！
くだんの`new FileReader(path)`にイチャモンを付けましたけど、だってこれ、try { … } の中に書いてないもん！`BufferedReader`をオートクローズしてもらいたくてtry-with-resources書いたんだもん！

# こんな書き方もできる

でも、`FileReader`も`AutoClosable`なんです。なので、以下のようにも書けます。

```java:Java9NewTryWithResourcesを一部書き換えてみた
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は<ruby>好<rt>よ</rt></ruby>くて、frはダメなの？」とか、「何が為のIOExceptionをキャッチ？しかもFileNotFoundExceptionってIOExceptionのサブクラスだし...」などとヤキモキしてしまうことになります。

# まとめ

Java9の新try-with-resources構文、<ruby>好<rt>い</rt></ruby>いんだか悪いんだか。悩みます。まとめになってなくて、すみません。🙇

以上です。