Java Tips | 文字列処理:小文字変換

Java Java
スポンサーリンク

小文字変換は「表記ゆれを“ゆるく受け止める”ための技

大文字変換が「全部そろえてガチっと比較する」イメージだとしたら、
小文字変換は「とりあえずぜんぶ丸めて、ゆるく受け止める」イメージに近いです。

GET, get, Get を同じメソッド名として扱いたい。
Json, JSON, json を同じ種別として扱いたい。
設定値で debug, DEBUG, Debug を全部「デバッグモード ON」として扱いたい。

こういうときに、「まず小文字にそろえてから考える」というのは、とてもシンプルで強力なパターンです。


Java の基本:String#toLowerCase を正しく理解する

まずは一番シンプルな形

Java で小文字変換をする基本メソッドは String#toLowerCase() です。

public class LowercaseBasic {

    public static void main(String[] args) {
        String s = "AbcDEF";
        String lower = s.toLowerCase();
        System.out.println(lower); // abcdef
    }
}
Java

これだけ見ると「簡単」で終わりそうですが、
業務でちゃんと使うなら、もう一歩だけ踏み込んでおきたいポイントがあります。

それが「ロケール(Locale)を意識する」という話です。


重要ポイント:toLowerCase は Locale を指定して使う

なぜ Locale を指定する必要があるのか

String#toLowerCase() には、引数なし版と toLowerCase(Locale locale) 版があります。
業務コードでは、基本的に「Locale を明示して呼ぶ」ことをおすすめします。

import java.util.Locale;

public class LowercaseLocale {

    public static void main(String[] args) {
        String s = "AbcDEF";

        String lowerDefault = s.toLowerCase();                 // デフォルトロケール依存
        String lowerRoot    = s.toLowerCase(Locale.ROOT);      // ロケール非依存

        System.out.println(lowerDefault); // 多くの環境で abcdef
        System.out.println(lowerRoot);    // 常に abcdef
    }
}
Java

言語によって「大文字・小文字の対応ルール」が微妙に違うため、
環境のデフォルトロケールに引きずられると、「国によって変換結果が違う」という事故が起こりえます。

英字コードや設定値、HTTP メソッド名など、「英語ベースの識別子」を小文字にそろえたいだけなら、
Locale.ROOT を指定しておくのが一番安全です。

String method = "GET";
String normalized = method.toLowerCase(Locale.ROOT); // "get"
Java

ここは地味ですが、「ロケールを明示するクセ」をつけておくと、後々のバグをかなり防げます。


ユーティリティ化:業務でよく使う「小文字変換」を名前付きにする

設定値・種別コード・メソッド名などをそろえる

業務では、「この値は小文字で扱う」と決めてしまうと楽になるものがいくつかあります。
例えば、設定値の on/off、ログレベルの info/warn/error、HTTP メソッドの get/post などです。

毎回 toLowerCase(Locale.ROOT) と書くのは冗長なので、ユーティリティにまとめてしまいます。

import java.util.Locale;

public final class Lowercases {

    private Lowercases() {}

    public static String lowerOrNull(String text) {
        if (text == null) {
            return null;
        }
        return text.toLowerCase(Locale.ROOT);
    }

    public static String lowerTrimmedOrNull(String text) {
        if (text == null) {
            return null;
        }
        return text.trim().toLowerCase(Locale.ROOT);
    }
}
Java

使い方はこうなります。

String level = "  WARN ";
String normalized = Lowercases.lowerTrimmedOrNull(level);

System.out.println("\"" + normalized + "\""); // "warn"
Java

ここでの重要ポイントは二つあります。

一つ目は、「null をどう扱うかをユーティリティ側で決めている」ことです。
lowerOrNull は null をそのまま返し、lowerTrimmedOrNull はトリムしてから小文字にする、というルールをメソッド名と実装で固定しています。

二つ目は、「トリム+小文字変換という“よくある前処理セット”を一箇所に閉じ込めている」ことです。
設定値や種別コードの正規化では、「前後の空白を削る」「小文字にそろえる」がセットで出てくるので、
それをユーティリティとして名前付きにしておくと、コードが一気に読みやすくなります。


例題:設定値を小文字にそろえて判定する

「on / ON / On / true / TRUE / 1」を全部同じ扱いにする

設定ファイルや環境変数で、ON/OFF や true/false を表す文字列がバラバラに書かれていることがあります。
これを安全に解釈するユーティリティを、小文字変換を使って書いてみます。

import java.util.Locale;

public final class FlagParser {

    private FlagParser() {}

    public static boolean isEnabled(String value) {
        if (value == null) {
            return false;
        }
        String v = value.trim().toLowerCase(Locale.ROOT);
        return "on".equals(v) || "true".equals(v) || "1".equals(v);
    }
}
Java

使い方はこうです。

System.out.println(FlagParser.isEnabled("on"));      // true
System.out.println(FlagParser.isEnabled("ON"));      // true
System.out.println(FlagParser.isEnabled(" True "));  // true
System.out.println(FlagParser.isEnabled("0"));       // false
System.out.println(FlagParser.isEnabled(null));      // false
Java

ここで深掘りしたいのは、「小文字変換は“判定ロジックの前処理”として使うと威力を発揮する」という点です。
trimtoLowerCase(Locale.ROOT) を一箇所に閉じ込めておくことで、
設定値の解釈がプロジェクト全体で一貫し、「この値は大文字でも小文字でもいい」という柔軟さを安全に持てます。


例題:ファイル拡張子を小文字にそろえて扱う

「.JPG / .jpg / .Jpeg」を同じものとして扱う

ファイル名の拡張子判定も、小文字変換と相性がいい典型例です。

import java.util.Locale;

public final class FileTypes {

    private FileTypes() {}

    public static boolean isJpeg(String filename) {
        if (filename == null) {
            return false;
        }
        String lower = filename.toLowerCase(Locale.ROOT);
        return lower.endsWith(".jpg") || lower.endsWith(".jpeg");
    }
}
Java

使い方はこうです。

System.out.println(FileTypes.isJpeg("photo.JPG"));     // true
System.out.println(FileTypes.isJpeg("photo.jpeg"));    // true
System.out.println(FileTypes.isJpeg("photo.PnG"));     // false
Java

ここでのポイントは、「拡張子の比較をする前に、小文字にそろえてしまう」というシンプルさです。
endsWith(".JPG") || endsWith(".jpg") || ... のように分岐を増やすより、
「まず小文字にする」というルールを決めてしまったほうが、読みやすくてバグも入りにくくなります。


例題:HTTP メソッド名を小文字にそろえてルーティングする

GET / get / Get を全部同じ扱いにする

外部から来る HTTP リクエストのメソッド名は、基本的には大文字ですが、
テストコードやモックなどで小文字が混ざることもあります。

ルーティングの前に小文字にそろえてしまえば、実装がすっきりします。

import java.util.Locale;

public final class HttpMethodRouter {

    private HttpMethodRouter() {}

    public static String normalizeMethod(String method) {
        if (method == null) {
            return "";
        }
        return method.trim().toLowerCase(Locale.ROOT);
    }

    public static void route(String method) {
        String m = normalizeMethod(method);
        switch (m) {
            case "get" -> System.out.println("handle GET");
            case "post" -> System.out.println("handle POST");
            default -> System.out.println("method not supported: " + m);
        }
    }
}
Java

使い方はこうです。

HttpMethodRouter.route("GET");   // handle GET
HttpMethodRouter.route("get");   // handle GET
HttpMethodRouter.route(" Post "); // handle POST
HttpMethodRouter.route("DELETE"); // method not supported: delete
Java

ここでの重要ポイントは、「normalizeMethod という“前処理専用メソッド”を用意している」ことです。
ルーティングの本体は「小文字のメソッド名だけを相手にすればよい」状態になり、
余計な大小比較やトリム処理がスイッチ文の中に入り込まなくなります。


まとめ:小文字変換ユーティリティで身につけたい感覚

小文字変換は、「人間には同じに見えるけれど、プログラム的には違う表記」をゆるく受け止めるための技です。

押さえておきたい感覚は、まず「toLowerCase はロケールに依存するので、業務では toLowerCase(Locale.ROOT) を基本形にする」こと。
次に、「トリムや null ハンドリングと組み合わせた“前処理セット”をユーティリティとして名前付きにし、設定値・拡張子・HTTP メソッドなどで共通利用する」こと。
そして、「“比較のたびにごちゃごちゃ条件を書く”のではなく、“まず小文字にそろえてからシンプルに比較する”というスタイルをプロジェクトの標準にする」ことです。

もしあなたのコードのどこかに、s.toLowerCase()equalsIgnoreCase がバラバラに散らばっているなら、
その一つを題材にして、ここで作った LowercasesFlagParserFileTypesHttpMethodRouter のようなユーティリティにまとめてみてください。
それだけで、「読みやすくて、バグりにくくて、仕様変更にも強い文字列処理」に、一段レベルアップできます。

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