PC パソコン

Kotlin「Variable must be initialized」の原因と解決方法

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

このエラーは、変数を宣言したものの、値が代入されていない状態でその変数を使用しようとした場合などに発生します。

Kotlinでは、変数が使用される時点で必ず値を持っていることが求められるため、コンパイラが「値が設定されていない可能性がある」と判断するとエラーになります。

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

「Variable must be initialized」とは

「Variable must be initialized」は、日本語では「変数を初期化する必要があります」という意味のエラーです。

Kotlinでは、ローカル変数などを宣言しただけでは、その変数を自由に使用できるわけではありません。
実際に変数を参照する前に、値が代入されている必要があります。

fun main() {
    var message: String

    println(message)
}

この例では、messageという変数を宣言していますが、値を代入しないままprintln()で使用しています。

そのため、コンパイラはmessageに値が入っていないと判断し、「Variable must be initialized」というエラーを表示します。

原因1:変数を初期化せずに使用している

もっとも基本的な原因は、変数を宣言した後に値を代入せず、そのまま使用していることです。

fun main() {
    var number: Int

    println(number)
}

この場合、numberには値が設定されていません。

次のように、宣言時に初期値を設定すれば解決できます。

fun main() {
    var number: Int = 0

    println(number)
}

型を推論できる場合は、型の記述を省略することもできます。

fun main() {
    var number = 0

    println(number)
}

原因2:条件分岐によって値が代入されない可能性がある

変数に値を代入しているように見えても、条件分岐によっては値が設定されない可能性がある場合、エラーになることがあります。

fun main() {
    val score = 80
    var result: String

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

    println(result)
}

このコードでは、score >= 60が成立した場合にはresultへ値が代入されます。

しかし、条件が成立しなかった場合にはresultへ何も代入されません。
そのため、コンパイラはprintln(result)の時点で値が存在しない可能性があると判断します。

このような場合は、elseを追加して、すべての条件で値が設定されるようにします。

fun main() {
    val score = 80
    var result: String

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

    println(result)
}

if式を使って直接初期化する

Kotlinでは、ifを式として使用できるため、条件分岐の結果をそのまま変数へ代入できます。

fun main() {
    val score = 80

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

    println(result)
}

この書き方では、どちらの条件でも必ず値が返されるため、初期化漏れを防ぎやすくなります。

原因3:whenの一部の条件でしか代入していない

whenを使用している場合も、すべての可能性で値が設定されないコードでは注意が必要です。

fun main() {
    val number = 2
    var message: String

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

    println(message)
}

この場合、numberが1または2以外だった場合には、messageへ値が代入されません。

elseを追加して、すべての場合に値を設定します。

fun main() {
    val number = 2
    var message: String

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

    println(message)
}

さらに、whenを式として利用すると、より簡潔に記述できます。

fun main() {
    val number = 2

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

    println(message)
}

原因4:クラスのプロパティを初期化していない

クラス内のプロパティについても、通常は宣言時またはコンストラクタなどで初期化する必要があります。

class User {
    var name: String
}

このように、非null型のStringを値なしで宣言すると、初期化されていないためエラーになります。

もっとも単純な解決方法は、初期値を設定することです。

class User {
    var name: String = ""
}

コンストラクタで初期化する

クラスのプロパティは、コンストラクタから値を受け取る方法も一般的です。

class User(val name: String)

fun main() {
    val user = User("Taro")

    println(user.name)
}

この場合、Userのインスタンスを作成するときに必ずnameが設定されます。

initブロックで初期化する

プロパティの値をインスタンス生成時に計算する場合などは、initブロックを使用することもできます。

class User(firstName: String, lastName: String) {

    val fullName: String

    init {
        fullName = "$firstName $lastName"
    }
}

initブロック内で必ず値が設定されるため、fullNameを使用できます。

lateinitを使用する方法

Androidアプリなどでは、変数を宣言した時点では値を設定できず、後から必ず初期化するケースがあります。

そのような場合は、lateinitを使用できます。

lateinit var message: String

fun initialize() {
    message = "Hello"
}

lateinitを付けることで、「この変数は後から初期化する」とコンパイラへ伝えることができます。

注意:
lateinitを使用しても、実際に値を代入する前に変数へアクセスすると、
UninitializedPropertyAccessExceptionが発生します。

そのため、単にエラーを消す目的でlateinitを付けるのではなく、必ず初期化される処理になっているか確認することが重要です。

nullable型として宣言する方法

変数が「まだ値を持っていない」という状態そのものに意味がある場合は、nullable型として宣言する方法もあります。

var message: String? = null

fun main() {
    println(message)
}

String?nullを許可する型なので、初期値としてnullを設定できます。

ただし、nullable型を使用すると、その後の処理ではnullを考慮する必要があります。

var message: String? = null

fun main() {
    println(message?.length)
}

このようにセーフコール?.などを使用して処理します。

lateinitとnullableのどちらを使うか

lateinitとnullable型は似た場面で使用されることがありますが、意味は異なります。

「後から必ず値が設定され、それ以降はnullとして扱いたくない」という場合は、lateinitが適していることがあります。

一方、「値が存在しない状態も正常な状態として扱う」場合は、nullable型を使用するほうが自然です。

単に初期化エラーを回避するためだけにlateinitやnullable型へ変更するのではなく、その変数が本来どのような状態を持つべきなのかを考えて選択することが重要です。

Variable must be initializedが表示されたときの確認ポイント

「Variable must be initialized」が表示された場合は、まずエラーになっている変数の宣言位置と、その変数を最初に使用している位置を確認します。

  • 変数を宣言しただけで値を設定していない
  • ifの一部の条件でしか値を代入していない
  • whenの一部の条件でしか値を代入していない
  • クラスのプロパティに初期値がない
  • コンストラクタやinitブロックで初期化されていない
  • 後から初期化する設計なのにlateinitなどを使用していない

特に、条件分岐があるコードでは「自分が想定している条件では値が入る」だけではなく、コンパイラから見てすべての実行経路で値が設定されるかを確認することが重要です。

まとめ

Kotlinの「Variable must be initialized」は、変数が使用される時点で値が設定されていない、または設定されていない可能性がある場合に表示されるエラーです。

基本的には、変数の宣言時に初期値を設定するか、ifwhenなどのすべての分岐で値が代入されるようにすることで解決できます。

クラスのプロパティでは、初期値、コンストラクタ、initブロックなどを利用して初期化できます。また、後から値を設定する必要がある場合には、設計に応じてlateinitやnullable型を使用できます。

エラーが表示されたときは、エラー箇所だけを見るのではなく、変数の宣言から使用されるまでの処理を確認し、「使用時点までに必ず値が設定されるか」を確認すると原因を見つけやすくなります。