PC パソコン

Kotlin「Val cannot be reassigned」の原因と解決方法

Kotlinでコードを記述していると、「Val cannot be reassigned」というエラーが表示されることがあります。

このエラーは、valで宣言した変数に対して、あとから別の値を代入しようとした場合などに発生します。

Kotlinでは、valで宣言した変数は一度値を設定すると再代入できません。
そのため、値を変更する必要がある場合はvarを使用するなど、変数の用途に応じた宣言が必要です。

この記事では、Kotlinで「Val cannot be reassigned」が表示される主な原因と、基本的な解決方法を紹介します。

「Val cannot be reassigned」とは

「Val cannot be reassigned」は、日本語では「valには再代入できません」という意味のエラーです。

Kotlinでは、変数を宣言するときにvalまたはvarを使用します。

valは一度値を設定すると別の値を再代入できません。
一方、varはあとから値を変更できます。

fun main() {
    val number = 10

    number = 20
}

この例では、numbervalで宣言した後に、20を再代入しようとしています。

valには再代入できないため、「Val cannot be reassigned」というエラーが表示されます。

原因1:valで宣言した変数に再代入している

もっとも基本的な原因は、valで宣言した変数に対して、あとから別の値を代入していることです。

fun main() {
    val message = "Hello"

    message = "Goodbye"
}

この場合、messageには最初に"Hello"が設定されています。

その後で"Goodbye"を代入しようとしているため、エラーになります。

値を変更する必要がある場合は、valではなくvarで宣言します。

fun main() {
    var message = "Hello"

    message = "Goodbye"

    println(message)
}

varで宣言した変数には、あとから別の値を代入できます。

valとvarの違い

Kotlinでは、変数の値を変更する必要があるかどうかによって、valvarを使い分けます。

val name = "Taro"
var age = 20

age = 21

namevalで宣言されているため、別の文字列を再代入できません。

一方、agevarで宣言されているため、20から21へ値を変更できます。

値を変更する必要がない場合は、基本的にvalを使用すると、意図しない再代入を防ぎやすくなります。

原因2:条件分岐の中でvalへ再代入している

ifなどの条件分岐の中で、すでに値が設定されているvalへ再代入している場合もエラーになります。

fun main() {
    val result = "未判定"
    val score = 80

    if (score >= 60) {
        result = "合格"
    }
}

このコードでは、resultは宣言時に"未判定"で初期化されています。

その後、ifの中で"合格"を再代入しようとしているため、エラーになります。

値を変更する設計であれば、varを使用します。

fun main() {
    var result = "未判定"
    val score = 80

    if (score >= 60) {
        result = "合格"
    }

    println(result)
}

if式を使ってvalへ直接代入する

値を途中で変更する必要がなく、条件によって初期値だけを決めたい場合は、ifを式として使用できます。

fun main() {
    val score = 80

    val result = if (score >= 60) {
        "合格"
    } else {
        "不合格"
    }

    println(result)
}

この書き方では、条件判定の結果がそのままresultの初期値になります。

そのため、resultvarに変更してあとから再代入する必要がありません。

原因3:whenの中でvalへ再代入している

whenの中で、すでに初期化されているvalへ別の値を代入しようとした場合も同じエラーが発生します。

fun main() {
    val message = "Unknown"
    val number = 1

    when (number) {
        1 -> message = "One"
        2 -> message = "Two"
    }
}

この場合、messageにはすでに"Unknown"が設定されているため、whenの中で再代入できません。

値を変更する必要がある場合は、varを使用します。

fun main() {
    var message = "Unknown"
    val number = 1

    when (number) {
        1 -> message = "One"
        2 -> message = "Two"
    }

    println(message)
}

when式を使って初期化する

whenも式として使用できるため、条件によって値を決めるだけであれば、valの初期化時に直接利用できます。

fun main() {
    val number = 1

    val message = when (number) {
        1 -> "One"
        2 -> "Two"
        else -> "Unknown"
    }

    println(message)
}

この方法では、messageへの再代入が必要ないため、valのまま使用できます。

原因4:ループ内でvalを更新しようとしている

繰り返し処理の中で値を変更しようとする場合も、valで宣言していると再代入できません。

fun main() {
    val count = 0

    for (i in 1..5) {
        count = count + 1
    }
}

このコードでは、ループのたびにcountの値を変更しようとしています。

値を繰り返し更新する必要があるため、varを使用します。

fun main() {
    var count = 0

    for (i in 1..5) {
        count = count + 1
    }

    println(count)
}

次のようにインクリメント演算子を使用することもできます。

fun main() {
    var count = 0

    for (i in 1..5) {
        count++
    }

    println(count)
}

valでもオブジェクトの内容を変更できる場合がある

valは「値そのものを変更できない」というより、変数が参照している対象を別のものへ再代入できないという意味です。

そのため、変更可能なオブジェクトをvalで保持している場合、そのオブジェクトの内部状態を変更できることがあります。

fun main() {
    val numbers = mutableListOf(1, 2, 3)

    numbers.add(4)

    println(numbers)
}

この場合、numbers自体はvalで宣言されていますが、MutableListの内容を変更することはできます。

一方で、別のリストをnumbersへ再代入することはできません。

fun main() {
    val numbers = mutableListOf(1, 2, 3)

    numbers = mutableListOf(4, 5, 6)
}

この場合は、numbersへ別のオブジェクトを再代入しようとしているため、「Val cannot be reassigned」が発生します。

valで宣言したからといって、参照先のオブジェクトが必ず変更不可能になるわけではありません。
valが禁止しているのは、その変数に別の値や別の参照を再代入することです。

プロパティのvalへ再代入している場合

クラスのプロパティをvalで宣言している場合も、初期化後に別の値へ変更することはできません。

class User {
    val name = "Taro"

    fun changeName() {
        name = "Jiro"
    }
}

この場合、namevalとしてすでに初期化されているため、changeName()内で再代入できません。

プロパティの値をあとから変更する必要がある場合は、varで宣言します。

class User {
    var name = "Taro"

    fun changeName() {
        name = "Jiro"
    }
}

valをvarに変更すればよいとは限らない

「Val cannot be reassigned」が表示された場合、valvarへ変更すればエラー自体は解消できることがあります。

ただし、本来変更する必要がない値までvarにすると、意図しない場所で値が変更される可能性があります。

条件によって値を決めるだけであれば、if式やwhen式を使用して、最初から適切な値でvalを初期化できないか確認することも重要です。

値をあとから変更する必要がある場合はvarを使用し、初期化後に値を変更する必要がない場合はvalを使用するのが基本です。

Val cannot be reassignedが表示されたときの確認ポイント

「Val cannot be reassigned」が表示された場合は、エラーになっている変数がvalvarのどちらで宣言されているかを確認します。

  • valで宣言した変数へ別の値を再代入していないか
  • ifwhenの中で再代入していないか
  • ループ内で値を更新しようとしていないか
  • クラスのvalプロパティをあとから変更しようとしていないか
  • 値を変更する設計であればvarを使用すべきではないか
  • 再代入せず、if式やwhen式で初期化できないか

単純にvalvarへ変更するだけではなく、その変数が本当に途中で変更される必要があるのかを確認すると、より適切な修正ができます。

まとめ

Kotlinの「Val cannot be reassigned」は、valで宣言された変数に対して、初期化後に別の値を再代入しようとした場合に表示されるエラーです。

値をあとから変更する必要がある場合は、varで宣言することで再代入できるようになります。

一方、条件によって初期値を決めるだけであれば、if式やwhen式を使用することで、valのまま再代入せずに記述できます。

また、valで保持している変更可能なオブジェクトでは、オブジェクト内部の内容を変更できる場合がありますが、変数そのものへ別のオブジェクトを再代入することはできません。

エラーが表示されたときは、変数を単純にvarへ変更する前に、その値を本当に変更する必要があるのかを確認し、用途に応じてvalvarを使い分けることが重要です。