Java 11 → 17 の追加言語仕様

# [JEP361: Switch Expressions](https://openjdk.org/jeps/361)

switch 文に新しい表記が追加
- case ラベルについて新しい表記 (`case XXX ->`) を追加
    - break 文が不要になる
    - カンマ区切りで複数の値を指定可能
- 直接値を返すことが可能になった

<details><summary>サンプルコード</summary><div>

```java
public class SwitchExpressions {
	public static void main(String[] args) {
		newLabel();
		returnValue();
	}

	/**
	 * case XX: の代用として case XX -> が登場
	 * case に複数の値をカンマ区切りで指定できる
	 *
	 * メリット："->" の右側のコードのみ実行される (= break 文が不要)
	 */
	public static void newLabel() {
		switch (Calendar.getInstance().get(DAY_OF_WEEK)) {
			case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> {
				System.out.println("平日です。");
				System.out.println("ブロックにすれば複数命令も実行できます。");
			}
			case SATURDAY, SUNDAY -> System.out.println("休日です。");
		}

		// もちろん今まで通りの書き方もできる
		int dayOfWeek = SUNDAY;
		switch (dayOfWeek) {
			case SATURDAY:
			case SUNDAY:
				System.out.println("休日");
				break;
			default:
				System.out.println("平日");
		}
	}

	/**
	 * switch 文から値を返すことが可能。
	 * 条件によって代入する値を変える処理が簡潔に書けるようになった。
	 */
	public static void returnValue() {
		String dayOfWeek = switch (Calendar.getInstance().get(DAY_OF_WEEK)) {
			case MONDAY -> "月曜";
			case TUESDAY -> "火曜";
			case WEDNESDAY -> "水曜";
			case THURSDAY -> "木曜";
			case FRIDAY -> "金曜";
			case SATURDAY, SUNDAY -> "休日";
			default -> "";
		};
		System.out.println("今日は" + dayOfWeek);
	}
}
```
</div></details>

# [JEP 378: Text Blocks](https://openjdk.org/jeps/378)
複数行にわたる文字列やダブルクォーテーションを含む文字列を簡潔に記述できる。
- `“““` (ダブルクォーテーション 3 つ) で囲む
- 開始 `“““` の後ろには改行が必要
- 定義した文字列のインデントは閉じ `“““` の位置に合わせて自動調整されるっぽい

関連するものとして String に 3 つのメソッドが追加。
- String::formatted(Object... args)
    - 文字列に変数の値を埋め込む
- String::translateEscapes()
    - エスケープシーケンスが特殊文字として翻訳される
- String::stripIndent()
    - text blocks 同士を結合した場合などの余分な空白の削除

<details><summary>サンプルコード</summary><div>

```java
public class TextBlocks {
	public static void main(String[] args) {
		html();
		autoIndent();
		multiDoubleQuote();
		additionalMethods();
	}

	public static void html() {
		String str =
				"""
				 <html>
					 <body>
						 <p>Hello, world</p>
						 <input type="text">
					 </body>
				 </html>
				 """;
		System.out.println(str);
	}

	public static void autoIndent() {
		String str =
				"""
				line1: インデントなし
				  line2: 半角空白でインデント
					line3: タブ文字でインデント
				""";
		System.out.println(str);
	}

	public static void multiDoubleQuote() {
		// """ を文字列として扱いたい場合はいずれかの " をエスケープする必要がある
		String str =
				"""
				1: "
				2: ""
				3: ""\"
				4: ""\""
				""";
		System.out.println(str);
	}
}
```
</div></details>

# [JEP 394: Pattern Matching for instanceof](https://openjdk.org/jeps/394)

`obj instanceof class 変数名` で型判定した後にキャストして変数に代入してくれる。

<details><summary>サンプルコード</summary><div>

```java
public class PatternMatchingForInstanceof {
	public static void main(String[] args) {
		print("hoge");
		print(123);
	}

	public static void print(Object obj) {
		// instanceof [class] tmp で型チェックとキャストを同時に可能
		if (obj instanceof String s) {
			System.out.println(s + " is String.");
		} else {
			System.out.println("obj is not String.");
		}
		/* 以前は型チェック後に自分でキャストが必要だった
		if (obj instanceof String) {
			String s = (String) obj;
			...
		 */
	}
}
```
</div></details>

# [JEP 395: Records](https://openjdk.org/jeps/395)

record クラスが正式に導入された。
以下を自動生成してくれる。
- コンストラクタ
- フィールドの getter
    - メソッド名はフィールド名と同じ
- equals(), hashCode(), toString()

<details><summary>サンプルコード</summary><div>

```java
public class RecordClass {
	public static void main(String[] args) {
		User user = new User("山田 太郎", new Address("123-4567", "東京"));

		// 各フィールドの値の取得
		System.out.println("user.name(): " + user.name());
		System.out.println("user.home().zip(): " + user.home().zip());
		System.out.println("user.home().address(): " + user.home().address());

		User user2 = new User("山田 太郎", new Address("123-4567", "東京"));
		System.out.println("user.hashCode(): " + user.hashCode());
		System.out.println("user.equals(user2): " + user.equals(user2));
		System.out.println("user.toString(): " + user.toString());
	}
}

public record User(String name, Address home) {}

public record Address(String zip, String address) {}
```
</div></details>

# [JEP 409: Sealed Classes](https://openjdk.org/jeps/409)

自クラスを継承するサブクラスを限定することができる。
- sealed class [class name] permits [subclass name] で継承するサブクラスを限定する
- permits に指定するサブクラスと、自クラスを継承しているサブクラスは完全一致させる必要がある
    - permits に指定したサブクラスは自クラスを継承して存在している必要がある
    - permits に指定していないクラスは自クラスを継承できない
- 自クラスとサブクラスは同じ階層に配置する必要がある
- サブクラスは final にする必要がある = サブクラスを更に継承することは禁止

設計思想としては「enum なら種類数まで限定できるけど、継承ってその情報欠けてるよね？」という感じらしい。
　例）太陽系の惑星を enum で定義すれば水星～海王星の 8 つであることが自明になるが、
　　　惑星クラスを水星クラス、金星クラス、…、海王星クラスとしてクラス定義すると
　　　惑星は 8 つであるという情報が欠落する。

<details><summary>サンプルコード</summary><div>

```java
public abstract sealed class SealedClasses
		permits Mercury, Venus, Earth {
	// 本例では abstract クラスにしているが、別に普通のクラスでも interface でも OK

	protected String name;

	public static void main(String[] args) {
		SealedClasses planet = new Mercury("水星");
		planet.printName();
	}

	public SealedClasses(String name) {
		this.name = name;
	}

	public void printName() {
		System.out.println("This planet is " + name + ".");
	}
}

public final class Mercury extends SealedClasses {
	public Mercury(String name) {
		super(name);
	}
}

public final class Venus extends SealedClasses {
	public Venus(String name) {
		super(name);
	}
}

public final class Earth extends SealedClasses {
	public Earth(String name) {
		super(name);
	}
}
```
</div></details>

# [JEP 406: Pattern Matching for switch (Preview)](https://openjdk.org/jeps/406)

:::note warn
Java17 時点ではプレビュー、Java19 でも 3rdプレビュー機能
:::

switch 文の case に null、クラス名を指定できる。
- クラス名を指定する場合はキャストした値を受け取る変数名も一緒に記述する

&& で条件を繋ぐこともできる。

<details><summary>サンプルコード</summary><div>

```java
public class PatternMatchingForSwitch {
	public static void main(String[] args) {
		System.out.println(branchByClass(null));
		System.out.println(branchByClass(10));
		System.out.println(branchByClass(12345L));
		System.out.println(branchByClass("hogefugafoo"));
		System.out.println(branchByClass("hoge"));
		System.out.println(branchByClass(3.14));
	}

	/**
	 * case の条件部分にクラスと null を指定できる
	 *  -> case [class名] [変数名] の形
	 * また、&& で条件を繋ぐこともできる
	 */
	public static String branchByClass(Object obj) {
		return switch (obj) {
			case null      -> String.format("null");
			case Integer i -> String.format("int %d, *10 %d", i, i*10);
			case Long l    -> String.format("long %d", l);
			case String s && s.length() >= 5 -> String.format("5 文字以上の文字列 %s", s);
			case String s  -> String.format("String %s", s);
			default -> String.format("non match %s", obj);
		};
	}
}
```
</div></details>