テンプレート展開は「決まった型に値を流し込む」技術
テンプレート展開は、
「ひな形(テンプレート)となる文字列の中に、動的な値を埋め込んで完成形の文字列を作る」テクニックです。
メール本文の定型文に名前や日付を差し込む。
エラーメッセージの中にパラメータ値を入れる。
ログメッセージにIDや状態を埋め込む。
こういう「毎回ほぼ同じだけど、一部だけ違う文字列」を作るときに、
テンプレート展開はものすごく役に立ちます。
ポイントは、
「テンプレートの書き方」と
「値をどう渡すか」と
「未設定の項目をどう扱うか」
この3つをきちんと決めてユーティリティ化することです。
一番シンプルな形:{name} のようなプレースホルダを置き換える
まずは Map を使った基本的なテンプレート展開
よくある書き方は、テンプレートの中に {name} や {date} のような「プレースホルダ」を書いておき、
そこに Map で渡した値を埋め込む方法です。
import java.util.Map;
public final class TemplateEngine {
private TemplateEngine() {}
public static String expand(String template, Map<String, String> values) {
if (template == null) {
return null;
}
if (values == null || values.isEmpty()) {
return template;
}
String result = template;
for (Map.Entry<String, String> entry : values.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
if (value == null) {
value = "";
}
String placeholder = "{" + key + "}";
result = result.replace(placeholder, value);
}
return result;
}
}
Java使い方の例を見てみます。
String template = "こんにちは {name} さん。本日は {date} です。";
Map<String, String> values = Map.of(
"name", "山田",
"date", "2026-02-04"
);
String message = TemplateEngine.expand(template, values);
System.out.println(message);
// → こんにちは 山田 さん。本日は 2026-02-04 です。
Javaここで押さえておきたい重要ポイントは三つあります。
一つ目は、「テンプレートはただの文字列」であることです。
特別なクラスではなく、String に {name} のような印を埋め込んでいるだけです。
二つ目は、「値は Map で渡す」という設計にしていることです。
これにより、プレースホルダ名と値の対応がはっきりし、
順番に依存しない書き方ができます。
三つ目は、「null の値は空文字にする」というポリシーを決めていることです。
ここをどうするかで、テンプレート展開の挙動が変わります。
「null のときはそのまま {name} を残す」「例外にする」なども選択肢ですが、
まずはシンプルに「空文字にする」から始めると扱いやすいです。
未設定のプレースホルダをどう扱うかを決める
置き換えられなかった {xxx} をそのまま残すか、エラーにするか
先ほどの実装では、Map に存在するキーだけを置き換えています。
つまり、テンプレートに {age} があっても、Map に "age" がなければそのまま残ります。
String template = "名前: {name}, 年齢: {age}";
Map<String, String> values = Map.of("name", "山田");
String result = TemplateEngine.expand(template, values);
System.out.println(result);
// → 名前: 山田, 年齢: {age}
Javaこれをどう捉えるかは、要件次第です。
「未設定のプレースホルダが残っていたらバグなので、例外にしたい」
「未設定のものは空文字にしてしまいたい」
「未設定のものは特定の文字("-" など)にしたい」
こういったルールを、ユーティリティ側で決めておくと、
業務コードがスッキリします。
例えば、「未設定のプレースホルダがあったら例外にする」版はこう書けます。
public static String expandStrict(String template, Map<String, String> values) {
String result = expand(template, values);
// まだ {xxx} が残っているかチェック
int idx = result.indexOf('{');
if (idx != -1 && result.indexOf('}', idx) != -1) {
throw new IllegalStateException("Unresolved placeholder exists: " + result);
}
return result;
}
Javaここでの重要ポイントは、
「テンプレート展開の“失敗”をどう検知するか」を決めていることです。
その場しのぎで replace を書くのではなく、
「展開しきれなかったらエラー」というルールをユーティリティに閉じ込めることで、
バグを早く見つけられるようになります。
プレースホルダの書式を正規表現でしっかり扱う
{name} 以外の文字列に誤爆しないようにする
先ほどの実装は、単純に result.replace("{" + key + "}", value) を使っていました。
これは簡単ですが、場合によっては「意図しない部分まで置き換えてしまう」ことがあります。
例えば、テンプレートに {id} というプレースホルダと、"{id}-backup" のような文字列が混在している場合、
どこまでがプレースホルダなのかをきちんと定義しておく必要があります。
より厳密にやるなら、正規表現で「{ と } に囲まれた英数字+アンダースコア」をプレースホルダとみなす、
といったルールを作ることもできます。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class TemplateEngineRegex {
private static final Pattern PLACEHOLDER =
Pattern.compile("\\{([a-zA-Z0-9_]+)\\}");
private TemplateEngineRegex() {}
public static String expand(String template, Map<String, String> values) {
if (template == null) {
return null;
}
if (values == null || values.isEmpty()) {
return template;
}
Matcher matcher = PLACEHOLDER.matcher(template);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String key = matcher.group(1);
String value = values.getOrDefault(key, "");
if (value == null) {
value = "";
}
matcher.appendReplacement(sb, Matcher.quoteReplacement(value));
}
matcher.appendTail(sb);
return sb.toString();
}
}
Javaここでの重要ポイントは二つです。
一つ目は、「プレースホルダの“形”を正規表現で定義している」ことです。{abc} はOKだが {a-b} はNG、など、ルールをはっきりさせられます。
二つ目は、「Matcher.quoteReplacement を使って、値の中に $ や \ があっても安全に置き換えている」ことです。
正規表現ベースの置き換えでは、値の中身が正規表現のメタ文字として解釈されないように注意が必要です。
実務でのテンプレート展開の使いどころ
例1:メール本文のテンプレート
例えば、こんなテンプレートを用意しておきます。
String template = """
{userName} 様
いつもご利用ありがとうございます。
{date} 時点のご利用状況は以下の通りです。
・プラン: {planName}
・利用回数: {usageCount} 回
今後ともよろしくお願いいたします。
""";
Javaサーバ側では、次のように展開します。
Map<String, String> values = Map.of(
"userName", "山田太郎",
"date", "2026-02-04",
"planName", "プレミアム",
"usageCount", "12"
);
String body = TemplateEngineRegex.expand(template, values);
System.out.println(body);
Javaこうしておくと、
「テンプレートの中身(文章)」と
「埋め込む値(ロジック)」をきれいに分離できます。
例2:ログメッセージの整形
ログ出力でも、テンプレート展開はよく使えます。
String template = "処理ID={id}, 状態={status}, 所要時間={time}ms";
Map<String, String> values = Map.of(
"id", "JOB-20260204-001",
"status", "SUCCESS",
"time", "1234"
);
String logMessage = TemplateEngine.expand(template, values);
logger.info(logMessage);
Javaここでのポイントは、
「ログのフォーマットをテンプレートとして固定し、
値だけを差し替える」というスタイルにしておくと、
ログ解析や検索がしやすくなることです。
まとめ:テンプレート展開ユーティリティで身につけたい感覚
テンプレート展開は、
「ほぼ同じ形の文字列に、毎回違う値を流し込む」ための基本テクニックです。
大事なのは、
テンプレートの書式({name} など)を決めること。
値は Map で渡し、順番ではなく名前で紐づけること。
未設定のプレースホルダをどう扱うか(残す・空文字・エラー)を決めること。
必要なら正規表現でプレースホルダの形を厳密に定義すること。
もしあなたのコードのどこかに、
String msg = "こんにちは " + name + " さん。本日は " + date + " です。";
Javaのような「文字列連結だらけ」の行があったら、
それをテンプレート展開に置き換えてみてください。
その小さな一歩が、
「文章とロジックをきれいに分離できるエンジニア」への、
確かなステップになります。
