Java 17の新機能まとめ — sealedクラス・テキストブロック・パターンマッチングを整理
はじめに
Java 17はLTS(長期サポート)バージョンで、現在の実務プロジェクトでも広く採用されています。
この記事では、Java 17で使えるようになった主要な新機能を3つ取り上げて、実務での使いどころを整理します。
| 機能 | 正式採用バージョン |
|---|---|
| テキストブロック | Java 15 |
| パターンマッチング(instanceof) | Java 16 |
| sealedクラス | Java 17 |
1. テキストブロック — 複数行文字列をすっきり書く
複数行の文字列をこれまでどう書いていましたか?
// 旧来の書き方:エスケープと連結だらけ
String json = "{\n" +
" \"name\": \"田中太郎\",\n" +
" \"age\": 25\n" +
"}";
テキストブロックを使うと:
// テキストブロック:読みやすい
String json = """
{
"name": "田中太郎",
"age": 25
}
""";
""" で囲むだけです。\n や \" のエスケープが不要になります。
実務での使いどころ
// SQLクエリ
String sql = """
SELECT u.id, u.name, u.email
FROM users u
WHERE u.active = true
AND u.age >= 20
ORDER BY u.name
""";
// HTMLテンプレート
String html = """
<html>
<body>
<h1>%s</h1>
<p>%s</p>
</body>
</html>
""".formatted(title, body);
注意: インデントは """ の終端位置に合わせて自動で除去されます。
2. パターンマッチング(instanceof) — キャストを1行に
これまでの instanceof チェックはこう書いていました:
// 旧来の書き方:instanceof → キャスト → 使用 の3ステップ
Object obj = "Hello, Java";
if (obj instanceof String) {
String s = (String) obj; // キャストが必要
System.out.println(s.toUpperCase());
}
パターンマッチングを使うと:
// パターンマッチング:instanceof + キャストを1行に
Object obj = "Hello, Java";
if (obj instanceof String s) { // sに直接束縛される
System.out.println(s.toUpperCase());
}
さらに条件も一緒に書けます:
// 型チェック + 条件を同時に
if (obj instanceof String s && s.length() > 5) {
System.out.println("長い文字列: " + s);
}
実務での使いどころ
// 複数の型を処理する場合
public String describe(Object obj) {
if (obj instanceof Integer i) {
return "整数: " + i;
} else if (obj instanceof String s) {
return "文字列: " + s;
} else if (obj instanceof List<?> list) {
return "リスト(" + list.size() + "件)";
}
return "その他: " + obj;
}
3. sealedクラス — 継承できるクラスを制限する
sealed を使うと、どのクラスがこのクラスを継承できるかを明示的に宣言できます。
// sealedクラスの宣言:permitsで継承を許可するクラスを列挙
public sealed class Shape permits Circle, Rectangle, Triangle {
public abstract double area();
}
// 許可されたサブクラス
public final class Circle extends Shape {
private final double radius;
public Circle(double radius) {
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public final class Rectangle extends Shape {
private final double width;
private final double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
permits に書かれていないクラスは Shape を継承できません:
// コンパイルエラー:Squareはpermitsに含まれていない
public class Square extends Shape { ... } // ❌
sealedと一緒に使うサブクラスの修飾子
| 修飾子 | 意味 |
|---|---|
final |
これ以上継承不可 |
sealed |
さらに継承を制限する(孫クラスも制御) |
non-sealed |
制限を解除(誰でも継承可能) |
実務での使いどころ
ドメインモデルで「取りうる状態を明示したい」場面に有効です:
// 注文ステータスの表現
public sealed class OrderStatus permits Pending, Approved, Shipped, Cancelled {
public abstract String label();
}
public final class Pending extends OrderStatus {
@Override public String label() { return "受付中"; }
}
public final class Approved extends OrderStatus {
@Override public String label() { return "承認済み"; }
}
permits に宣言した型だけが存在できるため、コンパイラが「漏れ」を検知してくれます。
まとめ
| 機能 | 解決する問題 | 一言 |
|---|---|---|
| テキストブロック | 複数行文字列のエスケープ地獄 |
""" で囲むだけ |
| パターンマッチング | instanceof → キャストの冗長さ | 型チェックと変数束縛を1行に |
| sealedクラス | 継承の範囲が不明確 |
permits で継承先を宣言 |
Java 17はLTSなので、現場でも積極的に使われています。特にテキストブロックとパターンマッチングは導入コストが低く、すぐに使えます。コードが一気にすっきりするので、ぜひ試してみてください。