Java Tips | 文字列処理:スネークケース変換

Java Java
スポンサーリンク

スネークケース変換は「Java の名前を“DB や設定ファイルの世界”に合わせる」技

キャメルケース変換が「外の世界を Java っぽくする」技だとしたら、
スネークケース変換はその逆で、「Java の名前を DB や設定ファイルの世界に合わせる」技です。

userNameuser_name
UserNameuser_name
createdAtcreated_at

こんな変換ができると、次のような場面でとても楽になります。

  • Java のフィールド名から DB カラム名を自動生成したい
  • DTO のプロパティ名から JSON のキー名(user_name)を作りたい
  • 設定クラスのフィールド名から、環境変数名(USER_NAME)を作りたい

ここでは、「キャメルケース → スネークケース」を軸に、実務で使えるユーティリティを組み立てていきます。


まず「スネークケースって何?」をざっくり整理する

snake_case と SCREAMING_SNAKE_CASE

スネークケース(snake_case)は、単語をアンダースコア _ でつなぐ書き方です。

user_name
created_at
order_item_id

さらに、全部大文字にした USER_NAME のような形は、
「SCREAMING_SNAKE_CASE(スクリーミングスネークケース)」と呼ばれ、
環境変数名や定数名でよく使われます。

今回のユーティリティでは、

  • camelCase / PascalCase → snake_case
  • camelCase / PascalCase → SCREAMING_SNAKE_CASE

の両方を作れるようにしておくと、業務でかなり使い回せます。


基本方針:「大文字の“境目”で区切って、つなぎ直す」

いきなり置換しないで「境目を見つける」ことから始める

キャメルケース → スネークケース変換の本質は、
「大文字が出てきたところを“単語の境目”とみなして、アンダースコアを挟む」ことです。

userName
u ser N ame とバラすのではなく、
userName に分けたい。

createdAt
createdAt に分けたい。

つまり、「小文字+大文字」の境目を見つけて、そこに _ を入れるイメージです。
これを正規表現で表現すると、かなりスッキリ書けます。


実装ステップ1:キャメルケースを snake_case に変換する

正規表現で「小文字と大文字の境目」にアンダースコアを挟む

まずは、userNameuser_name のような基本変換を実装します。

import java.util.Locale;

public final class SnakeCases {

    private SnakeCases() {}

    public static String toSnake(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        // 1. "userName" → "user_Name" のように、境目にアンダースコアを挟む
        String withUnderscore = text.replaceAll("([a-z0-9])([A-Z])", "$1_$2");
        // 2. 全体を小文字にする
        return withUnderscore.toLowerCase(Locale.ROOT);
    }
}
Java

使い方はこうです。

System.out.println(SnakeCases.toSnake("userName"));      // user_name
System.out.println(SnakeCases.toSnake("UserName"));      // user_name
System.out.println(SnakeCases.toSnake("createdAt"));     // created_at
System.out.println(SnakeCases.toSnake("userID"));        // user_id
System.out.println(SnakeCases.toSnake("URLValue"));      // url_value
Java

ここで深掘りしたい重要ポイントは二つあります。

一つ目は、「([a-z0-9])([A-Z]) という正規表現で、“小文字または数字の直後に大文字が来る場所”を見つけている」ことです。
userName なら rN のところでマッチし、$1_$2 によって r_Name になります。
createdAt なら dA のところで d_At になります。

二つ目は、「アンダースコアを挟んだあとで、全体を小文字にしている」ことです。
UserName のように先頭が大文字でも、User_Nameuser_name となり、
URLValue のような略語も URL_Valueurl_value という形に落ち着きます。

この「境目に _ を挟んでから、小文字に落とす」という二段構えを覚えておくと、
キャメルケース → スネークケース変換のロジックが頭の中でスッと整理されます。


実装ステップ2:SCREAMING_SNAKE_CASE に変換する

snake_case をそのまま大文字にするだけでいい

USER_NAME のような SCREAMING_SNAKE_CASE は、
「snake_case を全部大文字にしたもの」と考えるとシンプルです。

import java.util.Locale;

public final class SnakeCases {

    private SnakeCases() {}

    public static String toSnake(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        String withUnderscore = text.replaceAll("([a-z0-9])([A-Z])", "$1_$2");
        return withUnderscore.toLowerCase(Locale.ROOT);
    }

    public static String toScreamingSnake(String text) {
        String snake = toSnake(text);
        if (snake == null) {
            return null;
        }
        return snake.toUpperCase(Locale.ROOT);
    }
}
Java

使い方はこうです。

System.out.println(SnakeCases.toScreamingSnake("userName"));   // USER_NAME
System.out.println(SnakeCases.toScreamingSnake("createdAt"));  // CREATED_AT
System.out.println(SnakeCases.toScreamingSnake("userID"));     // USER_ID
Java

ここでのポイントは、「SCREAMING_SNAKE_CASE は“snake_case のバリエーション”として扱う」ことです。
toSnake を土台にして、その結果を大文字にするだけにしておくと、
ロジックが一箇所にまとまり、メンテナンスしやすくなります。


例題:Java フィールド名から DB カラム名を作る

userName → user_name を自動でやりたい

実務で一番よくあるのが、「Java のフィールド名は userName だけど、DB のカラム名は user_name」というパターンです。
マッピングライブラリを使わない場合でも、「名前変換ルール」だけ自前で持っておきたいことがあります。

public final class ColumnNames {

    private ColumnNames() {}

    public static String fieldToColumn(String fieldName) {
        return SnakeCases.toSnake(fieldName);
    }
}
Java

使い方はこうです。

System.out.println(ColumnNames.fieldToColumn("userName"));     // user_name
System.out.println(ColumnNames.fieldToColumn("createdAt"));    // created_at
System.out.println(ColumnNames.fieldToColumn("userId"));       // user_id
Java

ここでの重要ポイントは、「“フィールド名→カラム名”という文脈をメソッド名に刻んでいる」ことです。
中身は単に toSnake を呼んでいるだけですが、
fieldToColumn という名前がついているだけで、「これはDBとの橋渡しなんだな」と一目で分かります。


例題:フィールド名から環境変数名(SCREAMING_SNAKE_CASE)を作る

userName → USER_NAME のような変換

設定クラスのフィールド名を、そのまま環境変数名にマッピングしたい、というケースもよくあります。

public final class EnvNames {

    private EnvNames() {}

    public static String fieldToEnv(String fieldName) {
        return SnakeCases.toScreamingSnake(fieldName);
    }
}
Java

使い方はこうです。

System.out.println(EnvNames.fieldToEnv("userName"));    // USER_NAME
System.out.println(EnvNames.fieldToEnv("dbUrl"));       // DB_URL
System.out.println(EnvNames.fieldToEnv("maxRetryCount"));// MAX_RETRY_COUNT
Java

ここでのポイントは、「“環境変数は SCREAMING_SNAKE_CASE で書く”というルールをコードに落とし込んでいる」ことです。
プロジェクト全体でこのユーティリティを使えば、
「このフィールドはどんな環境変数名で上書きできるのか」が、自然と一貫した形になります。


例題:JSON のキー名を snake_case にそろえる

Java のプロパティ名をそのまま JSON のキーに使いたくないとき

API の仕様で、「JSON のキーは snake_case で書く」というルールになっていることがあります。
その場合、Java のプロパティ名(userName)から JSON のキー名(user_name)を作るユーティリティがあると便利です。

public final class JsonKeys {

    private JsonKeys() {}

    public static String fieldToJsonKey(String fieldName) {
        return SnakeCases.toSnake(fieldName);
    }
}
Java

使い方はこうです。

System.out.println(JsonKeys.fieldToJsonKey("userName"));   // user_name
System.out.println(JsonKeys.fieldToJsonKey("createdAt"));  // created_at
Java

ここでのポイントは、「“JSON のキー名は snake_case”という仕様を、ユーティリティ名と変換ロジックで固定している」ことです。
あとから「やっぱり camelCase にしたい」となったら、
fieldToJsonKey の中身だけ差し替えれば、呼び出し側のコードはそのままで仕様変更に追従できます。


エッジケースと設計のポイント

すでに snake_case の文字列が来たらどうするか

入力がすでに user_name だった場合、toSnake("user_name") はどうなるか。
正規表現は「小文字+大文字」の境目だけを対象にしているので、
user_name はそのまま user_name として返ってきます。

これは、「“すでに snake_case っぽいものは壊さない”」という意味で、実務的には良い挙動です。

略語(URL, ID, HTTP)をどう扱うか

userIDuser_id
URLValueurl_value

このあたりは、「略語を全部大文字で書くかどうか」によって好みが分かれます。
今の実装では、URLValueURL_Valueurl_value という流れになります。

もし「URLValueurl_value ではなく urlvalue にしたい」などの強いルールがあるなら、
略語リストを持っておいて、変換前に置き換える、といった工夫が必要になります。

大事なのは、「スネークケース変換は“命名規約をコードに落とし込んだもの”であり、プロジェクトごとのルールを反映させていく余地がある」と理解しておくことです。


まとめ:スネークケース変換ユーティリティで身につけたい感覚

スネークケース変換は、「Java の世界の名前(userName, createdAt)を、DB や設定・JSON の世界の名前(user_name, USER_NAME)に橋渡しする」ための技です。

押さえておきたい感覚は、まず「小文字(または数字)と大文字の境目に _ を挟み、そのあとで小文字に落とす」という二段構えのパターン。
次に、「toSnake を土台にして、toScreamingSnake のようなバリエーション(環境変数用など)を重ねる」という設計。
そして、「fieldToColumnfieldToEnvfieldToJsonKey のような“文脈付きの薄いラッパー”を用意して、命名規約をユーティリティとしてプロジェクト全体で共有する」ことです。

もしあなたのコードのどこかに、replaceAll("([a-z])([A-Z])", "$1_$2") のような断片がバラバラに散らばっているなら、
それを題材にして、ここで作った SnakeCasesColumnNamesEnvNamesJsonKeys にまとめてみてください。
それだけで、「読みやすくて、再利用できて、命名ルールがコードとして生きている」文字列処理に、一段レベルアップできます。

タイトルとURLをコピーしました