Kotlinにおける文字列比較は非常に洗練されており、Javaよりも安全で簡潔に書ける一方、仕様を正しく理解していないと誤用につながります。
この記事では、
- Kotlin公式仕様に沿った正確な説明
- Javaとの違い
- 実務での推奨パターン
- よくある誤解・アンチパターン
を踏まえ、「もう迷わない」レベルまで詳しく解説します。
目次
KotlinのStringの基本仕様
KotlinのStringは次の特徴を持ちます。
- イミュータブル(不変)
- JVMでは
java.lang.Stringと完全互換 - 値比較と参照比較が明確に分離されている
- null安全な比較が標準で可能
特に重要なのが、演算子 == の意味がJavaと異なる点です。
== と === の違い(最重要)
==:構造的等価(値・内容の比較)
val a = "Kotlin"
val b = "Kotlin"
println(a == b) // true
- 文字列の中身(内容)が同じかを比較
- 内部的には
equals()が呼ばれる - null安全
実際には、次のように変換されます。
a == b
// ↓
a?.equals(b) ?: (b === null)
nullを含む場合
val a: String? = null
val b: String? = null
println(a == b) // true
val a: String? = null
println(a == "Kotlin") // false(例外なし)
NullPointerExceptionが起きないのがKotlinの大きな強みです。
===:参照等価(同一インスタンスか)
val a = "Kotlin"
val b = String("Kotlin".toCharArray())
println(a === b) // false
- メモリ上で同じオブジェクトかを比較
- 実務で使うことはほぼない
- テスト・最適化・特殊なケース向け
文字列比較に === を使うのは基本的に誤りです。
Javaとの決定的な違い
Java
if (a.equals(b)) { ... }
Kotlin
if (a == b) { ... }
Kotlinでは
「== が値比較」
「equals() を直接呼ぶ必要はほぼない」
という設計思想になっています。
大文字・小文字を無視した比較
equals(ignoreCase = true)(基本かつ安全)
val a = "kotlin"
val b = "KOTLIN"
println(a.equals(b, ignoreCase = true)) // true
- Kotlinが提供する正規のAPI
- ASCII中心の比較に安全
- nullableでも使用可能(
String?に定義されている)
大小無視の比較はこれが第一選択
lowercase() / uppercase() の正しい理解(重要修正版)
よくある誤解
「lowercase() はロケール依存で危険」
「トルコ語問題が起きやすい」
これは 現在のKotlinの推奨仕様とは異なります。
正しい仕様
- 旧API
toLowerCase()/toUpperCase()
→ デフォルトロケール依存・非推奨
- 新API(Kotlin 1.5以降)
lowercase()/uppercase()
→ 不変ロケール(invariant locale)を使用=ロケール非依存
val a = "KOTLIN"
println(a.lowercase()) // kotlin
これは ID・キー・タグ・プロトコル値の正規化に最適です。
ロケールを考慮すべきケース
ユーザー向け表示、自然言語処理などでは ロケールを明示します。
import java.util.Locale
text.lowercase(Locale.getDefault())
使い分け指針
| 用途 | 推奨 |
|---|---|
| プログラム内部の比較 | equals(ignoreCase = true) |
| 正規化・キー化 | lowercase() |
| 表示・言語依存処理 | lowercase(Locale) |
toLowerCase() | ❌ 非推奨 |
辞書順(順序)比較
compareTo()
val a = "apple"
val b = "banana"
println(a.compareTo(b)) // 負の値
0→ 等しい- 負数 → 左が小さい
- 正数 → 左が大きい
演算子で書ける(Kotlinらしい)
println(a < b) // true
println(a > b) // false
注意(nullable)
val a: String? = "apple"
val b: String? = "banana"
a < b // コンパイルエラー
順序比較はnull安全ではない
必要ならnullチェックやElvis演算子を使う
部分一致・前方一致・後方一致
"Hello Kotlin".contains("Kotlin") // true
"Hello Kotlin".startsWith("Hello") // true
"Hello Kotlin".endsWith("Kotlin") // true
すべて ignoreCase = true を指定可能。
正規表現による比較
val regex = Regex("\\d{3}-\\d{4}")
regex.matches("123-4567") // 完全一致
regex.containsMatchIn("ID:123") // 部分一致
入力バリデーションやフォーマット確認に便利です。
よくあるアンチパターン
=== を使う
if (a === b) { ... } // 誤用
Javaの癖で equals() を多用
a.equals(b) // Kotlinでは冗長なことが多い
非推奨APIの使用
a.toLowerCase() // 非推奨
実務でのベストプラクティスまとめ
| 目的 | 推奨 |
|---|---|
| 通常比較 | a == b |
| null含む比較 | a == b |
| 大小無視 | a.equals(b, true) |
| 正規化 | lowercase() |
| 辞書順 | a < b |
| 入力検証 | Regex |
Web・アプリ開発での実践ポイント
- ユーザー入力は
trim()してから比較 - APIレスポンスは仕様通りに正規化
- 比較ルール(大小無視など)は仕様として固定
- 表示処理と内部ロジックの比較を混同しない
まとめ
Kotlinの文字列比較は、
- 安全
- 簡潔
- 意図が明確
という強力な設計になっています。
== を恐れず使い、=== を正しく避け、ロケールと正規化を意識すれば、文字列比較でバグることはほぼなくなります。
以上、Kotlinの文字列比較についてでした。
最後までお読みいただき、ありがとうございました。










