---
title: switch式で始めるJava流パターンマッチング
tags:  #java #jjug_ccc #関数型プログラミング  
author: [シンプレクス株式会社](https://docswell.com/user/Simplex)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/Y76W4L9M7V.jpg?width=480
description: JJUG CCC 2026 Spring のセッション『switch式で始めるJava流パターンマッチング』の公開資料です。
published: May 29, 26
canonical: https://docswell.com/s/Simplex/K7NJ6N-simplex_ito01
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/Y76W4L9M7V.jpg)

switch式で始める
Java流パターンマッチング
Java 21 / switch expression / sealed + record
シンプレクス株式会社
伊藤悠椰
1
© 2026 Simplex Inc.


# Page. 2

![Page Image](https://bcdn.docswell.com/page/G75MQ1NQ74.jpg)

今日のゴール
01
パターンマッチングの発想
Javaにパターンマッチングを持ち込む
Java 21 の switch は、単に分岐の書き方が変わっただけではなく「パターンマッ
チング」の道具となった
sealed / record と組み合わせると、扱える形を型で閉じられる
実務では、複雑な分岐をコアロジックだけADT化すると効きやすい
2
© 2026 Simplex Inc.


# Page. 3

![Page Image](https://bcdn.docswell.com/page/9J29P15WER.jpg)

自己紹介と出会い
01
パターンマッチングの発想
シンプレクス株式会社 リードエンジニア（2023年新卒入社）
主に Java + TypeScript でのシステム開発に従事
学生時代に OCaml で関数型プログラミングに出会う
3
© 2026 Simplex Inc.


# Page. 4

![Page Image](https://bcdn.docswell.com/page/DEY45ZK9JM.jpg)

OCamlとの出会い
01
パターンマッチングの発想
値の「形」で分岐できる
let rec fib = function
| 0 -&gt; 0
| 1 -&gt; 1
| n when n &gt; 1 -&gt; fib (n - 1) + fib (n - 2)
| _ -&gt; invalid_arg &quot;fib: n must be &gt;= 0&quot;
;;
条件式を積み重ねるのではなく、受け入れるケースを並べる
分岐の見た目が「仕様の表」に近くなる
4
© 2026 Simplex Inc.


# Page. 5

![Page Image](https://bcdn.docswell.com/page/VJNYN3RD78.jpg)

パターンマッチングとは
01
パターンマッチングの発想
受理する形を宣言的に並べる式
構造、種類、値の内容で分岐する
分岐内では、取り出した値の型が確定する
BNF やオートマトンと同じように「許される形」を先に考える
入力を if で探す
形を match で並べる
:
:
条件を上から順に評価する
受理できるケースを列挙する
5
© 2026 Simplex Inc.


# Page. 6

![Page Image](https://bcdn.docswell.com/page/YE9PR9M8J3.jpg)

型で形を表す
01
パターンマッチングの発想
まず、入力として扱う値の種類を型として列挙する
Expr ::=
| Num
| Add(Expr, Expr)
| Sub(Expr, Expr)
| Mul(Expr, Expr)
| Div(Expr, Expr)
ADT： Num / Add のように「あり得る形」を列挙した型
AST：式やJSONのような木構造を、ADTなどで表したデータ
処理は、その形ごとのケース列挙になる
6
© 2026 Simplex Inc.


# Page. 7

![Page Image](https://bcdn.docswell.com/page/GE8DW9LZED.jpg)

分岐をコンパイラに検査させる
01
パターンマッチングの発想
コンパイラが分岐を検査しやすい
パターンマッチングは「ケースの列挙」なので、
網羅性：全ケースを書けているか
到達不能：先に広いケースを書いて、後続ケースが死んでいないか
型の整合：取り出した値の型が分岐内で確定する
を機械的に検査できる
let classify s =
match s with
| s -&gt; &quot;string: &quot; ^ s
| s when String.length s = 0 -&gt; &quot;&lt;blank&gt;&quot;
(* unused *)
7
© 2026 Simplex Inc.


# Page. 8

![Page Image](https://bcdn.docswell.com/page/LELMNWL17R.jpg)

Javaで使える材料
SECTION 02
8
© 2026 Simplex Inc.


# Page. 9

![Page Image](https://bcdn.docswell.com/page/4JMYX9M5JW.jpg)

Javaへ持ち帰る観点
Javaで使える材料
02
関数型言語の match を、そのまま Java に持ち込むわけではない
形を閉じる：存在するケースを sealed で限定する
分岐を表にする： switch 式でケースを並べる
取り出しを型に任せる：pattern switch / record pattern で値を取り出す
この3つが揃うと、Javaでも「読める分岐表」に近づく
9
© 2026 Simplex Inc.


# Page. 10

![Page Image](https://bcdn.docswell.com/page/PJR9NGVZ79.jpg)

Java 8〜21の流れ
Javaで使える材料
02
Java は段階的に「関数型ライク」な書き味を取り込んできた
version
Java 8
Java 14
Java 17
Java 21
使える材料
ラムダ式 / Stream API
switch 式
sealed / record
pattern switch
この話での役割
処理を値として扱いやすくする
分岐から値を返す
データの形を閉じる
型で分岐して値を取り出す
10
© 2026 Simplex Inc.


# Page. 11

![Page Image](https://bcdn.docswell.com/page/PEXQNXZ1JX.jpg)

switch式：値を返す分岐表
switch
02
Javaで使える材料
が式になると、分岐結果をそのまま値として扱える
String msg = switch (code) {
case 200 -&gt; &quot;OK&quot;;
case 404 -&gt; &quot;Not Found&quot;;
default -&gt; &quot;Other&quot;;
};
で分岐ごとの結果が明確になる
代入前の一時変数や break の管理が減る
match に近い「ケース列挙」の読み味になる
case -&gt;
11
© 2026 Simplex Inc.


# Page. 12

![Page Image](https://bcdn.docswell.com/page/3EK9NW8MED.jpg)

sealed + record：形を閉じる
02
Javaで使える材料
sealed interface Expr permits Lit, Add, Mul {}
record Lit(int n) implements Expr {}
record Add(Expr l, Expr r) implements Expr {}
record Mul(Expr l, Expr r) implements Expr {}
：許可された実装だけに限定する
record ：値を持つ不変データを短く書く
合わせると、JavaでADTに近い形を作れる
sealed
12
© 2026 Simplex Inc.


# Page. 13

![Page Image](https://bcdn.docswell.com/page/L73WV12275.jpg)

pattern switch：型で分岐
02
Javaで使える材料
String label(Object value) {
return switch (value) {
case Integer n -&gt; &quot;int: &quot; + n;
case String s -&gt; &quot;str: &quot; + s;
case null
-&gt; &quot;&lt;null&gt;&quot;;
default
-&gt; &quot;&lt;unknown&gt;&quot;;
};
}
とキャストを分離しなくてよい
分岐の中では変数の型が確定している
case null も分岐表の一部として明示できる
instanceof
13
© 2026 Simplex Inc.


# Page. 14

![Page Image](https://bcdn.docswell.com/page/87DK8XZ6JG.jpg)

when / case null：穴を埋める
02
Javaで使える材料
String normalize(String s) {
return switch (s) {
case null -&gt; &quot;&lt;null&gt;&quot;;
case String x when x.isBlank() -&gt; &quot;&lt;blank&gt;&quot;;
case String x -&gt; x;
};
}
null を switch の外で特別扱いしなくてよい
ガード条件は when でケースに添えられる
Javaらしい null 対応を残しつつ、分岐表として読める
14
© 2026 Simplex Inc.


# Page. 15

![Page Image](https://bcdn.docswell.com/page/VJPK8PYZE8.jpg)

小さなADT例：Expr
Expr
Javaで使える材料
02
は、式の木構造を表すASTでもあり、形を閉じたADTでもある
sealed interface Expr permits Lit, Add, Mul {}
record Lit(int n) implements Expr {}
record Add(Expr l, Expr r) implements Expr {}
record Mul(Expr l, Expr r) implements Expr {}
ASTとして見る：式を木構造のデータとして扱う
ADTとして見る：式としてあり得る形を型で列挙する
15
© 2026 Simplex Inc.


# Page. 16

![Page Image](https://bcdn.docswell.com/page/2EVVN2GMEQ.jpg)

Java版eval：switchでケース列挙
する
02
Javaで使える材料
int eval(Expr expr) {
return switch (expr) {
case Lit(int n) -&gt; n;
case Add(var l, var r) -&gt; eval(l) + eval(r);
case Mul(var l, var r) -&gt; eval(l) * eval(r);
};
}
が sealed なので、あり得る形が限定される
switch はその形を上から列挙する場所になる
新しい形を増やしたとき、処理漏れに気づきやすい
Expr
16
© 2026 Simplex Inc.


# Page. 17

![Page Image](https://bcdn.docswell.com/page/57GLKR8XEL.jpg)

Java流matchの最小セット
02
Javaで使える材料
Javaでパターンマッチングっぽく書くための軸はシンプル
でデータの持ち方を明示する
sealed で存在する形を閉じる
switch 式で形ごとの処理を並べる
case null / when で実務上の例外も表に入れる
次は、この考え方をJSON探索に適用する
record
17
© 2026 Simplex Inc.


# Page. 18

![Page Image](https://bcdn.docswell.com/page/4EQYNV95JP.jpg)

実務例：JSON探索
SECTION 03
18
© 2026 Simplex Inc.


# Page. 19

![Page Image](https://bcdn.docswell.com/page/KJ4WGMXV71.jpg)

問題設定：user.nameを取り出す
実務例：JSON探索
03
以下のようなJSONレスポンスから user.name を安全に取り出したい
{
&quot;user&quot;: { &quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 30 },
&quot;status&quot;: &quot;active&quot;
}
JSONパース自体は Jackson 等のライブラリに任せる
問題は、その後のコアロジックでどんな形として扱うか
19
© 2026 Simplex Inc.


# Page. 20

![Page Image](https://bcdn.docswell.com/page/LE1YD8N47G.jpg)

Map/Objectのつらさ
Map&lt;String,Object&gt;
実務例：JSON探索
03
のまま進むと、期待する形が if の中に埋もれる
String readUserName(Object json) {
if (!(json instanceof Map&lt;?,?&gt; root)) return &quot;&lt;err&gt;&quot;;
Object userObj = root.get(&quot;user&quot;);
if (!(userObj instanceof Map&lt;?,?&gt; user)) return &quot;&lt;err&gt;&quot;;
Object nameObj = user.get(&quot;name&quot;);
if (!(nameObj instanceof String name)) return &quot;&lt;err&gt;&quot;;
return name;
}
cast / null / 欠損 / 型違いが同じ場所に混ざる
「本当に欲しい形」が読み取りづらい
20
© 2026 Simplex Inc.


# Page. 21

![Page Image](https://bcdn.docswell.com/page/GEWGYZ4ZJ2.jpg)

発想の転換：一度、閉じた型にする
03
実務例：JSON探索
境界で受けたデータを、コア向けの形に正規化する
外側 : JSON文字列 / JsonNode / Map&lt;String,Object&gt;
境界 : ライブラリでパースし、形を揃える
内側 : sealed interface J として扱う
I/Oやライブラリ呼び出しは既存の型でよい
複雑な分岐が集まるコアだけ、扱う形を閉じる
形が閉じると switch が分岐表として効いてくる
21
© 2026 Simplex Inc.


# Page. 22

![Page Image](https://bcdn.docswell.com/page/47ZLX12LJ3.jpg)

JSON値ADTを先に考える
03
実務例：JSON探索
パース後に扱うJSON値を、まず言語非依存のADTとして見る
Json =
Null
| Bool
of boolean
| Number of number
| String of text
| Array of Json list
| Object of (text * Json) list
これは入力文字列の文法ではなく、パース後のデータ型
Array と Object がまた Json を持つので木になる
Javaでは、このADTを sealed + record に落とす
22
© 2026 Simplex Inc.


# Page. 23

![Page Image](https://bcdn.docswell.com/page/YJ6W4LVMJV.jpg)

JavaでADTを表す
03
実務例：JSON探索
JSON値としてあり得る形を、Javaの型として閉じる
sealed interface J permits JNull, JBool, JNum, JStr, JArr, JObj {}
record JNull() implements J {}
record JBool(boolean b) implements J {}
record JNum(double n) implements J {}
record JStr(String s) implements J {}
record JArr(List&lt;J&gt; xs) implements J {}
record JObj(Map&lt;String, J&gt; fields) implements J {}
ここから先は Object ではなく J を相手に switch できる
23
© 2026 Simplex Inc.


# Page. 24

![Page Image](https://bcdn.docswell.com/page/GJ5MQ1DQJ4.jpg)

JSON探索実装
user.name
03
実務例：JSON探索
を安全に取り出す。欠損や型違いは empty に落とす
Optional&lt;String&gt; readUserName(J json) {
return switch (json) {
case JObj(var root) -&gt; switch (root.get(&quot;user&quot;)) {
case JObj(var user) -&gt; switch (user.get(&quot;name&quot;)) {
case JStr(var name) -&gt; Optional.of(name);
case null, default -&gt; Optional.empty();
};
case null, default -&gt; Optional.empty();
};
default -&gt; Optional.empty();
};
}
24
© 2026 Simplex Inc.


# Page. 25

![Page Image](https://bcdn.docswell.com/page/LE3WV1MZE5.jpg)

何が良くなったか
実務例：JSON探索
03
→ JObj → JStr という期待する形が、そのままコードに出る
Map#get の欠損は case null として分岐表に入る
型違いは default に落ち、キャスト失敗として散らばらない
処理の本題が「どの形を受け入れるか」に寄る
ifを消すことが目的ではなく、分岐の意味を表に出すことが目的
JObj
25
© 2026 Simplex Inc.


# Page. 26

![Page Image](https://bcdn.docswell.com/page/8EDK8X647G.jpg)

実務に入れる境界
03
実務例：JSON探索
全部をADTにする必要はない
境界：HTTP / JSON / DB / 外部ライブラリの型をそのまま使う
変換：境界で受けた形を、コア向けの結果型や状態型に寄せる
コア： sealed + record + switch で分岐を表にする
効果が出やすいのは、状態遷移・APIレスポンス処理・計算ルールのような分岐が密集
する場所
26
© 2026 Simplex Inc.


# Page. 27

![Page Image](https://bcdn.docswell.com/page/V7PK8PYVJ8.jpg)

応用例1：状態遷移
03
実務への展開
状態×イベントの分岐爆発は、switchの表にすると読みやすくなる
型定義は sealed State / sealed Event として閉じておく
State transition(State s, Event e) {
return switch (s) {
case Created __ -&gt; switch (e) {
case Pay __
-&gt; new Paid();
case Cancel __ -&gt; new Cancelled();
default
-&gt; throw new IllegalStateException();
};
case Paid __ -&gt; switch (e) {
case Ship __
-&gt; new Shipped();
case Cancel __ -&gt; new Cancelled();
default
-&gt; throw new IllegalStateException();
};
default -&gt; throw new IllegalStateException();
};
}
27
© 2026 Simplex Inc.


# Page. 28

![Page Image](https://bcdn.docswell.com/page/2JVVN2GRJQ.jpg)

応用例2：APIレスポンス処理
03
実務への展開
境界のレスポンスを、コア向けの Ok / Err(Reason) に正規化する
理由の種類を閉じておくと、以後の処理は理由ごとの表になる
String handle(ApiResult r) {
return switch (r) {
case Ok(var body) -&gt; &quot;OK: &quot; + body;
case Err(var reason) -&gt; switch (reason) {
case RateLimited __ -&gt; &quot;RETRY_LATER&quot;;
case NotFound __ -&gt; &quot;SHOW_404&quot;;
case ServerError(int status) -&gt;
&quot;ALERT(status=%d)&quot;.formatted(status);
};
};
}
28
© 2026 Simplex Inc.


# Page. 29

![Page Image](https://bcdn.docswell.com/page/5EGLKR86JL.jpg)

Javaの限界と強み
03
実務への展開
Javaの限界
のような標準コレクション分解は苦手
末尾最適化（TCO）は保証されないため、深い再帰は注意
head::tail
Javaの強み
/ record で設計を揃えるほど switch が強くなる
既存の型システム・IDE・静的解析と相性がよい
sealed
29
© 2026 Simplex Inc.


# Page. 30

![Page Image](https://bcdn.docswell.com/page/4JQYNV927P.jpg)

まとめ
03
Javaでパターンマッチングを実務に使う流れ
1. 形を閉じる： sealed / record で扱うケースを限定する
2. switchで並べる：形ごとの処理をケース列挙にする
3. 分岐を表にする：複雑な条件を、読める設計に寄せる
境界はライブラリでOK。コアだけADT化するのが、Java流パターンマッチングの現実
的な使いどころ
30
© 2026 Simplex Inc.
Final


# Page. 31

![Page Image](https://bcdn.docswell.com/page/K74WGMXPE1.jpg)

アンケートのお願い
03
Survey
ご参加ありがとうございました。フィードバックをお願いします
全体アンケート
セッションアンケート
https://x.gd/e6vlY
https://x.gd/SETKa
31
© 2026 Simplex Inc.


