Java Tips | 文字列処理:キャメルケース変換

Java Java
スポンサーリンク

キャメルケース変換は「バラバラな単語を“Java っぽい名前”にそろえる」技

業務システムを書いていると、こんな文字列がよく出てきます。

user_name
USER_NAME
user-name
user name

人間から見れば全部「ユーザー名」ですが、Java の世界では userName のような「キャメルケース(camelCase)」で書くのが一般的です。
外部システムやDBから来た「スネークケース」「ケバブケース」「スペース区切り」の文字列を、
Java のフィールド名・メソッド名・DTO のプロパティ名として扱いやすい形に変換する――それがキャメルケース変換ユーティリティの役割です。

ここでは、「キャメルケースって何?」から始めて、実務で使える変換ユーティリティを一緒に組み立てていきます。


キャメルケースの種類をざっくり整理する

lowerCamelCase と UpperCamelCase の違い

キャメルケースには、ざっくり二種類あります。

userName のように、先頭が小文字で始まり、単語の区切りごとに先頭を大文字にする形。
これは「lowerCamelCase(ローワーキャメルケース)」と呼ばれ、Java のフィールド名・メソッド名でよく使われます。

UserName のように、先頭も大文字で始まる形。
これは「UpperCamelCase(アッパーキャメルケース)」または「PascalCase」と呼ばれ、クラス名などでよく使われます。

今回のユーティリティでは、
「スネークケースなどの“単語が区切られた文字列”」を、
lowerCamelCaseUpperCamelCase のどちらにも変換できるようにしておくと実務で便利です。


基本方針:「単語に分解してから、組み立て直す」

いきなり変換しようとしないで「分解→整形→連結」

キャメルケース変換で一番大事なのは、いきなり文字列をゴリゴリ書き換えようとしないことです。
やるべきことはシンプルで、次の三段階に分けられます。

文字列を「単語」に分解する(区切り文字で split するイメージ)。
各単語を「全部小文字にする」「先頭だけ大文字にする」など、ルールに従って整形する。
整形した単語を、キャメルケースのルールに従って連結する。

この「分解→整形→連結」という流れをユーティリティとして固定しておくと、
スネークケースでもケバブケースでも、スペース区切りでも、同じパターンで処理できるようになります。


実装ステップ1:単語に分解するユーティリティ

区切り文字を正規表現でまとめて扱う

まずは、「単語に分解する」部分を作ります。
スネークケース(user_name)、ケバブケース(user-name)、スペース区切り(user name)などをまとめて扱いたいので、
_- やスペースなどを「区切り文字」として一括で split します。

import java.util.ArrayList;
import java.util.List;

public final class Words {

    private Words() {}

    public static List<String> splitToWords(String text) {
        List<String> result = new ArrayList<>();
        if (text == null || text.isEmpty()) {
            return result;
        }
        String[] parts = text.split("[_\\-\\s]+");
        for (String p : parts) {
            if (!p.isEmpty()) {
                result.add(p);
            }
        }
        return result;
    }
}
Java

使い方のイメージはこうです。

System.out.println(Words.splitToWords("user_name"));      // [user, name]
System.out.println(Words.splitToWords("USER-NAME"));      // [USER, NAME]
System.out.println(Words.splitToWords("user name id"));   // [user, name, id]
Java

ここで深掘りしたい重要ポイントは、「“区切り文字の扱い”を一箇所に閉じ込めている」ことです。
[_\\-\\s]+ という正規表現で、「アンダースコア」「ハイフン」「空白文字(スペース・タブなど)」をまとめて区切りとして扱っています。
あとから「ドットも区切りにしたい」「スラッシュも区切りにしたい」となっても、このメソッドだけ直せば済みます。


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

先頭だけ小文字、それ以外の単語は先頭大文字+残り小文字

次に、「分解された単語のリスト」を lowerCamelCase に組み立てるメソッドを作ります。

import java.util.List;
import java.util.Locale;

public final class CamelCases {

    private CamelCases() {}

    public static String toLowerCamel(String text) {
        List<String> words = Words.splitToWords(text);
        if (words.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();

        String first = words.get(0).toLowerCase(Locale.ROOT);
        sb.append(first);

        for (int i = 1; i < words.size(); i++) {
            String w = words.get(i);
            if (w.isEmpty()) {
                continue;
            }
            String lower = w.toLowerCase(Locale.ROOT);
            String camel = Character.toUpperCase(lower.charAt(0)) + lower.substring(1);
            sb.append(camel);
        }
        return sb.toString();
    }
}
Java

使い方はこうです。

System.out.println(CamelCases.toLowerCamel("user_name"));      // userName
System.out.println(CamelCases.toLowerCamel("USER_NAME"));      // userName
System.out.println(CamelCases.toLowerCamel("user-name-id"));   // userNameId
System.out.println(CamelCases.toLowerCamel(" user  name "));   // userName
Java

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

一つ目は、「最初の単語だけは全部小文字にして、そのまま先頭に置く」ことです。
これが userName の「user」の部分にあたります。

二つ目は、「2単語目以降は、“全部小文字にしたうえで、先頭だけ大文字にする”」ことです。
NAMENamename が来ても、最終的には Name になります。

三つ目は、「ロケールを明示して toLowerCase(Locale.ROOT) を使っている」ことです。
大文字・小文字変換はロケール依存の罠があるので、英字コードを扱うときは Locale.ROOT を指定するクセをつけておくと安全です。


実装ステップ3:UpperCamelCase に変換する

すべての単語の先頭を大文字にする

クラス名などで使う UpperCamelCase も、ほぼ同じパターンで書けます。

import java.util.List;
import java.util.Locale;

public final class CamelCases {

    private CamelCases() {}

    public static String toUpperCamel(String text) {
        List<String> words = Words.splitToWords(text);
        if (words.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (String w : words) {
            if (w.isEmpty()) {
                continue;
            }
            String lower = w.toLowerCase(Locale.ROOT);
            String camel = Character.toUpperCase(lower.charAt(0)) + lower.substring(1);
            sb.append(camel);
        }
        return sb.toString();
    }
}
Java

使い方はこうです。

System.out.println(CamelCases.toUpperCamel("user_name"));    // UserName
System.out.println(CamelCases.toUpperCamel("USER-NAME"));    // UserName
System.out.println(CamelCases.toUpperCamel("user name id")); // UserNameId
Java

ここでのポイントは、「lowerCamel と UpperCamel の違いは“最初の単語を小文字にするかどうかだけ”」ということです。
実装を見比べると、toLowerCamel では最初の単語だけ特別扱いしているのに対し、
toUpperCamel ではすべての単語を同じルールで処理しています。

この「最初の単語だけ特別扱い」という感覚を持っておくと、
キャメルケース変換の実装が頭の中でスッと整理されます。


例題:DB カラム名(スネークケース)を DTO のフィールド名に変換する

user_name → userName を自動でやりたい

実務でよくあるのが、「DB のカラム名は user_name だけど、Java のフィールド名は userName にしたい」というケースです。
マッピングライブラリを使う場合でも、「名前変換ルール」を自前で書く場面はよくあります。

public final class ColumnNameMapper {

    private ColumnNameMapper() {}

    public static String columnToField(String columnName) {
        return CamelCases.toLowerCamel(columnName);
    }
}
Java

使い方はこうです。

System.out.println(ColumnNameMapper.columnToField("user_name"));      // userName
System.out.println(ColumnNameMapper.columnToField("created_at"));     // createdAt
System.out.println(ColumnNameMapper.columnToField("USER_ID"));        // userId
Java

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


例題:JSON のキー名(スネークケース)を Java のクラス名に変換する

user_profile → UserProfile のようなクラス名を作る

コード生成ツールや簡易スクリプトで、「JSON のキーからクラス名を作りたい」ということもあります。

public final class JsonNameMapper {

    private JsonNameMapper() {}

    public static String keyToClassName(String key) {
        return CamelCases.toUpperCamel(key);
    }
}
Java

使い方はこうです。

System.out.println(JsonNameMapper.keyToClassName("user_profile")); // UserProfile
System.out.println(JsonNameMapper.keyToClassName("order_item"));   // OrderItem
Java

ここでのポイントは、「UpperCamelCase は“型名・クラス名”と相性がいい」ということです。
toUpperCamel を「クラス名用」として意識しておくと、
「この名前はクラス名なのか、フィールド名なのか」が頭の中で整理しやすくなります。


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

すでにキャメルケースっぽい文字列が来たらどうするか

例えば、入力がすでに userName だった場合、toLowerCamel("userName") はどう振る舞うべきか。
今の実装だと、Words.splitToWords は区切り文字でしか分割しないので、["userName"] という1単語扱いになります。
その結果、toLowerCamel はほぼそのまま userName を返します。

これは、「“すでにキャメルケースっぽいものは壊さない”」という意味で、実務的には悪くない挙動です。
もし「大文字と小文字の境目でも分割したい」という要件が出てきたら、
Words.splitToWords を拡張して、「userNameuserName に分ける」ようなロジックを足すこともできます。

数字や記号を含む場合の扱い

user_id2version_1_0 のように数字が混ざるケースもあります。
今の実装では、数字はそのまま単語の一部として扱われるので、

user_id2userId2
version_1_0version10

のような結果になります。

ここをどうするかは、プロジェクトのルール次第です。
version_1_0version1_0 のままがいい」なら、
区切り文字の定義や、数字の扱いを Words.splitToWords 側で調整していくことになります。

大事なのは、「キャメルケース変換は“万能変換”ではなく、“自分たちのプロジェクトでの命名ルールをコードに落とし込んだもの”だと捉える」ことです。


まとめ:キャメルケース変換ユーティリティで身につけたい感覚

キャメルケース変換は、「外の世界のバラバラな名前(user_name, USER-NAME, user name)を、Java の世界の名前(userName, UserName)にそろえる」ための技です。

押さえておきたい感覚は、まず「分解→整形→連結」という三段階で考えること。
次に、「区切り文字の扱いを Words.splitToWords のような一箇所に閉じ込めておき、スネーク・ケバブ・スペース区切りをまとめて処理する」こと。
そして、「toLowerCameltoUpperCamel を用意し、DB カラム名→フィールド名、JSON キー→クラス名など、文脈ごとの変換を薄いラッパーメソッドとして名前付きにする」ことです。

もしあなたのコードのどこかに、replace("_", "") しながら手作業で大文字・小文字をいじっている箇所があるなら、
それを題材にして、ここで作った WordsCamelCasesColumnNameMapperJsonNameMapper のようなユーティリティに置き換えてみてください。
それだけで、「読みやすくて、再利用できて、命名ルールがコードとして生きている」文字列処理に、一段レベルアップできます。

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