- valとvarの基本
- varの危険性
- valとvarの可読性の違い
Kotlinでは、変数の宣言にval
とvar
を使用します。
それぞれの特徴、利点、および注意点を具体的な例とともに解説します。
val
で宣言された変数は「再代入ができない」という制約がつきますが、以下の利点と欠点があります。
- 不変性の確保: 一度値を設定すると変更できないため、不変性が保証され、バグを防ぎやすくなります。
- スレッドセーフ: 不変オブジェクトはスレッドセーフであるため、複数のスレッドから同時にアクセスされても問題が発生しません。
- 読みやすさと理解のしやすさ: コードを読んだ人は、その変数が変更されないことを前提に理解できるため、コードの読みやすさが向上します
- 最適化: コンパイラやランタイムは、
val
を使ったコードをより効率的に最適化できる可能性があります。
- 柔軟性の欠如: 値を変更する必要がある場合には適していません。一度設定した値を変更することができないため、動的な変更が必要な場合には不便です。
- 初期化の制約: 初期化時に値を設定する必要があり、後から値を設定することができないため、柔軟な初期化が難しい場合があります。
- 再利用性の低下: 一度値を設定すると再代入できないため、再利用が難しくなる場合があります。
以下は、val
を使った変数宣言の具体例です。
val maxUsers = 100
fun getMaxUsers(): Int {
return maxUsers // ここでmaxUsersが変更される心配はありません
}
var
で宣言された変数は「再代入が可能」で、使い勝手が良さそうに感じますが、以下の利点と欠点があります。
- 柔軟性: 値を変更する必要がある場合に使用でき、動的な変更が必要な場合に便利です。
- 状態管理: 状態が変化するオブジェクト(カウンター、UIコンポーネントの状態など)を管理するのに適しています。
- 簡単な初期化: 後で値を設定する必要がある場合に、
var
を使うことで初期化が容易になります。
- 予期しない再代入: 誰かが変数の値を誤って再代入してしまうと、プログラムの意図しない動作を引き起こす可能性があります。
- スレッドセーフでない: 可変な変数は、複数のスレッドから同時にアクセスされた場合に競合状態が発生しやすくなり、データの一貫性が失われることがあります。
- コードの可読性低下:
var
を多用すると、どの時点で変数の値が変更されるかを追跡するのが難しくなり、コードの可読性が低下します。 - デバッグの難しさ: 変数の値が頻繁に変更されると、バグの原因を特定するのが難しくなります。
以下は、var
を使った変数宣言の具体例です。
var userCount = 0
fun addUser() {
userCount += 1 // ここでの再代入は問題ありません
}
fun resetUserCount() {
userCount = 0 // 意図しない場所で再代入されるリスクがあります
}
valを使うと、varを使ったときに比べてコードの読みやすさが向上します。
以下に具体例を用意しました。
ショッピングカートの合計金額を計算するが、途中で割引が適用される可能性がある
fun main() {
var totalPrice = 0.0 // 合計金額を再代入可能な変数として宣言
val items = listOf(
Pair("りんご", 150.0),
Pair("バナナ", 200.0),
Pair("さくらんぼ", 300.0)
)
for (item in items) {
totalPrice += item.second // 合計金額に各商品の価格を加算
}
println("割引前の合計金額: ¥$totalPrice")
// 途中で割引が適用される
if (totalPrice > 500) {
totalPrice *= 0.9 // 10%の割引を適用
}
println("割引後の合計金額: ¥$totalPrice")
}
fun main() {
val items = listOf(
Pair("りんご", 150.0),
Pair("バナナ", 200.0),
Pair("さくらんぼ", 300.0)
)
// sumByDoubleを使用して合計金額を計算
val totalPrice = items.sumByDouble { it.second }
println("割引前の合計金額: ¥$totalPrice")
// 割引後の価格を計算
val finalPrice = if (totalPrice > 500) {
totalPrice * 0.9 // 10%の割引を適用
} else {
totalPrice
}
println("割引後の合計金額: ¥$finalPrice")
}
私は、自分が書いたコードでも、他人が書いたコードでも最初は、出力されているデータや文字列を頼りに修正や機能の追加個所を探していきます。
例えば、割引後の合計金額に「消費税」を加えたい場合
varのケースでは、最後に出力される「割引後の合計金額」のコードを追っていきます。
この場合、totalPrice
を追っていくことになるのですが、totalPrice
の変更点が2箇所あり、安易にこの変数を修正するとどこまで影響が出るかがぱっとわかりません。
一方、valのケースでは、finalPrice
を追うことで「割引後の価格の計算」部分がわかります。
更に、そこで使われているtotalPrice
を追うことで、「純粋な合計処理」を見つける事ができます。
このように、valを使うことで、複数の処理が行われていても、変数が変更できないことで処理と処理の切れ目が明確になり、修正するときの影響範囲を明確に認識することができるようになります。
この記事では、Kotlinの変数宣言におけるval
とvar
の違いと、それぞれの利点および欠点、さらにはvarの危険性について解説しました。
val
は変更不可能な変数を宣言するために使用され、不変性を確保し、コードの安全性と読みやすさを向上させます。-
var
は変更可能な変数を宣言するために使用され、柔軟性がある一方で、予期しない再代入やスレッドセーフでないリスクがあります。
適切にval
とvar
を使い分けることで、より安全で可読性の高いコードを実現できます。