9.62: 他の型が適切な場所では、文字列を避ける
結論
文字列(String)は便利だが 「なんでも文字列で扱う」のは悪手。
型の持つ意味(検査可能性、制約、操作メソッド、表現の明確さ)を活用できず、バグ・パフォーマンス低下・セキュリティ問題・可読性低下を招く。
値に意味があるなら適切な型を使う(enum、Path、Duration、Instant、BigDecimal、UUID、Optional、数値/真偽値 など)。
良い例
意味のある型を使って安全で読みやすい API にする例。
import java.math.BigDecimal;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.util.UUID;
public class OrderService {
// 1) 通貨/金額は BigDecimal(または long cents)で扱う
public void chargeCustomer(UUID customerId, BigDecimal amount) {
// 金額の検証・丸めをここで行える
}
// 2) ファイルパスは Path にする(操作メソッドが豊富で型安全)
public void importFrom(Path csvFile) {
// Files.newBufferedReader(csvFile) などが直接使える
}
// 3) 時間は Duration / Instant を使う(パースや計算が簡単)
public void scheduleJob(Instant when, Duration timeout) { ... }
// 4) 識別子は UUID を使う(仕様が明確)
public Order findOrder(UUID id) { ... }
}
利点:
- コンパイル時に型の不整合を検出できる(早期発見)
- API 利用者に期待値が明確(「この引数はいつか?」→
Instant) - 標準 API・ユーティリティ(
Path,Duration,BigDecimal等)が使える(安全で効率的)
悪い例
すべて文字列で受け取ることによる典型的な問題。
public class BadApi {
// NG: everything as String
public void createOrder(String customerId, String amount, String when, String filePath) {
// 呼び出し側が "2025-01-01T10:00:00Z" や "100.00" を渡すが、
// どのフォーマットを期待しているかが不明で、実行時にパース失敗する可能性がある
}
}
問題点の例:
-
customerIdが UUID 形式でないと実行時エラー -
amountにカンマや通貨記号が入るとNumberFormatException -
whenの日付フォーマットの違いでDateTimeParseException -
filePathは OS 間で扱いが異なるがStringだと意図しない操作が起きやすい - 全て文字列にするとドキュメントやテストが必須になり、使い手のミスを防げない
まとめ
-
意味のある値には専用型を使う
- 日付/時刻 →
LocalDate/LocalDateTime/Instant - 期間 →
Duration - パス →
Path - 金額 →
BigDecimalまたは スケール付き整数(long cents) - 列挙される状態 →
enum - 一意 ID →
UUID(仕様上 UUID なら) - 真偽や数値 →
boolean/int/long等のプリミティブ
- 日付/時刻 →
-
文字列は“境界”だけに使う(JSON/HTTP/CLI の入出力やログ)。内部ロジックは型で扱う。
-
API の契約を明確にする:
引数型を通じて期待フォーマット・制約を表現する。加えて Javadoc で補足する。
-
変換・検証は入力境界で行う:
文字列→型 のパース・妥当性チェックは受け口で行い、内部は常に正しい型を扱う。
-
安全性と可読性が向上する設計を優先:
SQL やコマンド構築で文字列結合を行うと注入脆弱性になる(PreparedStatement やパラメータ化を使う)。
-
コレクション内の文字列(CSV 等)は専用構造に:
カンマ区切りStringよりList<String>/Set<String>を使う。
-
互換性・シリアライズの必要がある場合は DTO を用意:
外部とのやり取りは文字列ベースでも、内部には変換済み DTO/値オブジェクトを使う。