[Kotlinガイド #2] valとvar

この記事の内容

  • valとvarの基本
  • varの危険性
  • valとvarの可読性の違い

Kotlinの変数宣言: val と var

Kotlinでは、変数の宣言にvalvarを使用します。

それぞれの特徴、利点、および注意点を具体的な例とともに解説します。

valの利点と欠点

valで宣言された変数は「再代入ができない」という制約がつきますが、以下の利点と欠点があります。

valの利点

  • 不変性の確保: 一度値を設定すると変更できないため、不変性が保証され、バグを防ぎやすくなります。
  • スレッドセーフ: 不変オブジェクトはスレッドセーフであるため、複数のスレッドから同時にアクセスされても問題が発生しません。
  • 読みやすさと理解のしやすさ: コードを読んだ人は、その変数が変更されないことを前提に理解できるため、コードの読みやすさが向上します
  • 最適化: コンパイラやランタイムは、valを使ったコードをより効率的に最適化できる可能性があります。

valの欠点

  • 柔軟性の欠如: 値を変更する必要がある場合には適していません。一度設定した値を変更することができないため、動的な変更が必要な場合には不便です。
  • 初期化の制約: 初期化時に値を設定する必要があり、後から値を設定することができないため、柔軟な初期化が難しい場合があります。
  • 再利用性の低下: 一度値を設定すると再代入できないため、再利用が難しくなる場合があります。

valの具体例

以下は、valを使った変数宣言の具体例です。

Kotlin
val maxUsers = 100

fun getMaxUsers(): Int {
    return maxUsers // ここでmaxUsersが変更される心配はありません
}

varの利点と欠点

varで宣言された変数は「再代入が可能」で、使い勝手が良さそうに感じますが、以下の利点と欠点があります。

varの利点

  • 柔軟性: 値を変更する必要がある場合に使用でき、動的な変更が必要な場合に便利です。
  • 状態管理: 状態が変化するオブジェクト(カウンター、UIコンポーネントの状態など)を管理するのに適しています。
  • 簡単な初期化: 後で値を設定する必要がある場合に、varを使うことで初期化が容易になります。

varの欠点

  • 予期しない再代入: 誰かが変数の値を誤って再代入してしまうと、プログラムの意図しない動作を引き起こす可能性があります。
  • スレッドセーフでない: 可変な変数は、複数のスレッドから同時にアクセスされた場合に競合状態が発生しやすくなり、データの一貫性が失われることがあります。
  • コードの可読性低下: varを多用すると、どの時点で変数の値が変更されるかを追跡するのが難しくなり、コードの可読性が低下します。
  • デバッグの難しさ: 変数の値が頻繁に変更されると、バグの原因を特定するのが難しくなります。

varの具体例

以下は、varを使った変数宣言の具体例です。

Kotlin
var userCount = 0

fun addUser() {
    userCount += 1 // ここでの再代入は問題ありません
}

fun resetUserCount() {
    userCount = 0 // 意図しない場所で再代入されるリスクがあります
}

valを使うと可読性の向上が見込める

valを使うと、varを使ったときに比べてコードの読みやすさが向上します。

以下に具体例を用意しました。

ショッピングカートの合計金額を計算するが、途中で割引が適用される可能性がある

varバージョン

Kotlin
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")
}

valバージョン

Kotlin
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の変数宣言におけるvalvarの違いと、それぞれの利点および欠点、さらにはvarの危険性について解説しました。

  • valは変更不可能な変数を宣言するために使用され、不変性を確保し、コードの安全性と読みやすさを向上させます。
  • varは変更可能な変数を宣言するために使用され、柔軟性がある一方で、予期しない再代入やスレッドセーフでないリスクがあります。

適切にvalvarを使い分けることで、より安全で可読性の高いコードを実現できます。

参考リンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です