Kotlinを学び始めると、ほぼ必ず目にするキーワードが val です。
一見すると「varと何が違うのか」「Javaのfinalと同じなのか」と疑問に感じる方も多いでしょう。
結論から言うと、valは Kotlinの設計思想そのものを体現した重要なキーワード です。
単なる構文ではなく、安全性・可読性・保守性を高めるための基本戦略として使われます。
本記事では、valについて 基礎 → 応用 → 設計思想 → 実務での使い方 まで、体系的に解説します。
目次
valの基本的な意味
valは 再代入できない変数(読み取り専用の参照) を宣言するためのキーワードです。
val x = 10
x = 20 // コンパイルエラー
valは value の略- 最初に代入した値を、後から別の値に変更できない
- Kotlinでは 変数宣言の第一選択肢
val と var の違い
Kotlinでは変数宣言に val と var の2種類があります。
| キーワード | 再代入 | 用途 |
|---|---|---|
val | 不可 | 不変の参照 |
var | 可 | 可変の参照 |
var count = 0
count += 1 // OK
val total = 100
// total += 1 // NG
Kotlin流の考え方
「まず
valで書けないかを考え、どうしても必要な場合だけvarを使う」
これはKotlin公式ドキュメントや実務でも広く共有されている指針です。
val は定数ではない
非常によくある誤解ですが、valは定数ではありません。
val now = System.currentTimeMillis()
- 実行時に決まる値でも問題なし
- 「一度しか代入できない」だけ
コンパイル時定数は const val
const val MAX_COUNT = 100
const val は以下の条件を満たす必要があります。
- コンパイル時に値が確定する
- 型はプリミティブ型相当または
String - トップレベル、または
object内で宣言する
const val NAME = "Kotlin" // OK
// const val list = listOf(1, 2, 3) // NG
valでもオブジェクトの中身は変更できる
valは 参照の再代入を禁止するだけ です。
オブジェクト自体がミュータブルであれば、その中身は変更できます。
val list = mutableListOf(1, 2, 3)
list.add(4) // OK
これは次の操作だけが禁止されているためです。
list = mutableListOf(9, 9, 9) // NG(再代入)
完全に不変にしたい場合
val list = listOf(1, 2, 3)
// list.add(4) // コンパイルエラー
valとイミュータブル設計
Kotlinが val を強く推奨する理由は、バグを減らすためです。
fun calculate(price: Int): Int {
val tax = price * 10 / 100
return price + tax
}
このコードでは、
taxが途中で変更されない- 関数の処理が読みやすい
- 意図しない副作用が起きない
という利点があります。
なぜ重要か?
- 変更されない値は 思考コストが低い
- 並行処理や非同期処理でも安全
- レビュー時に「この値は変わらない」と即座に分かる
valとスマートキャストの関係
Kotlinには スマートキャスト という仕組みがあります。
val obj: Any = "Hello"
if (obj is String) {
println(obj.length)
}
これは、
objがval- ifブロック内で型が変わらないとコンパイラが保証できる
ために成立します。
補足(重要)
varでも、途中で変更されないと解析できる場合はスマートキャストされる- ただし
valの方が 安定してスマートキャストが効く - プロパティやマルチスレッドが絡むと
varは効かなくなることが多い
クラスプロパティとしての val
class User(
val name: String,
val age: Int
)
- 外部からは読み取り専用
- データクラスやDTOで多用される
val user = User("Taro", 20)
println(user.name)
// user.age = 30 // NG
計算プロパティとの相性も良い
val isAdult: Boolean
get() = age >= 20
- 状態を持たない
- 常に正しい値を返す
lateinit と val は併用できない
lateinit val name: String // コンパイルエラー
理由は明確で、
lateinit:後から代入する前提val:一度しか代入できない
代替案
val name: String by lazy {
loadName()
}
valは「後から1回だけ代入」できる(ローカル変数)
ローカル変数に限り、次のような書き方も可能です。
val x: Int
x = 10 // 初回代入はOK
// x = 20 // NG
- 使用前に必ず代入されている必要あり
- 「初期化を遅らせたいが再代入は不要」なケースで有効
実務でのベストプラクティス
実務指針まとめ
- 基本はすべて
val - 状態が変わる必要があるときだけ
var - コレクションは
- 参照:
val - 中身:用途に応じて可変 / 不変を選択
- 参照:
- public API・モデルクラスでは
valを優先
悪い例
var userName = "Taro" // 変更しないなら不要
良い例
val userName = "Taro"
Javaとの比較で理解する
| Java | Kotlin |
|---|---|
final int x = 10; | val x = 10 |
| 冗長 | 簡潔 |
| null安全なし | null安全あり(型で管理) |
まとめ
valは 再代入不可の参照- 定数ではない(定数は
const val) - オブジェクトの中身は変えられる
- Kotlinでは まず
valが基本 - 安全・可読・保守性を支える中核概念
以上、Kotlinのvalについてでした。
最後までお読みいただき、ありがとうございました。










