PC パソコン

Kotlin「Null can not be a value of a non-null type」の解決方法

Kotlinでコードを記述していると、 「Null can not be a value of a non-null type」 というエラーが表示されることがあります。

このエラーは、 nullを許可していない型の変数や引数などに、 nullを代入しようとした場合に発生します。

Kotlinでは、nullを許可する型と許可しない型が明確に区別されているため、 JavaなどからKotlinへ移行した場合には、 nullの扱いが原因でエラーになることがあります。

この記事では、Kotlinで 「Null can not be a value of a non-null type」が表示された場合に確認したい原因と、 基本的な解決方法を紹介します。

「Null can not be a value of a non-null type」とは

「Null can not be a value of a non-null type」は、 nullを格納できない型にnullを指定していることを示すエラーです。

例えば、次のようなコードがあるとします。

val name: String = null

このコードでは、 nameString型として定義されています。

KotlinのString型は、 そのままではnullを格納できません。

そのため、nullを代入しようとすると 「Null can not be a value of a non-null type」 というエラーになります。

Kotlinでは、nullを許可しない型をnon-null型、 nullを許可する型をnullable型として区別します。 nullを扱う可能性がある場合は、この違いを理解することが重要です。

StringとString?の違い

Kotlinでは、 StringString?は異なる型として扱われます。

val name: String = "Taro"

Stringはnullを許可しないため、 必ずString型の値を持つ必要があります。

一方、型名の後ろに ?を付けると、 nullを許可するnullable型になります。

val name: String? = null

この場合は、 nameにnullを代入できます。

原因1:non-null型の変数にnullを代入している

最も分かりやすい原因は、 nullを許可していない変数へ直接nullを代入しているケースです。

var message: String = null

Stringはnon-null型なので、 nullを代入することはできません。

nullを格納する必要がある場合は、 nullable型に変更します。

var message: String? = null

nullが不要な場合は、 初期値として適切なString型の値を指定します。

var message: String = ""

原因2:IntやBooleanなど他の型にnullを代入している

nullに関する制限はStringだけではありません。

Int、Long、Double、Booleanなども、 ?を付けていない場合はnullを格納できません。

val number: Int = null
val enabled: Boolean = null

nullを許可する必要がある場合は、 次のようにnullable型として定義します。

val number: Int? = null
val enabled: Boolean? = null

nullを格納する可能性がない値までnullable型にする必要はありません。 本当にnullという状態が必要なのかを確認したうえで型を決めることが重要です。

原因3:関数の引数がnon-null型になっている

関数の引数としてnullを渡した場合にも、 引数がnon-null型であればエラーになります。

fun showMessage(message: String) {
    println(message)
}

showMessage(null)

showMessage()の引数は String型なので、 nullを渡すことはできません。

nullを受け取る必要がある関数であれば、 引数をnullable型に変更します。

fun showMessage(message: String?) {
    println(message)
}

showMessage(null)

一方、関数の仕様としてnullを受け取る必要がない場合は、 呼び出し側でnull以外の値を渡すようにします。

原因4:関数の戻り値にnullを指定している

関数の戻り値をnon-null型として定義しているにもかかわらず、 nullをreturnするとエラーになります。

fun getName(): String {
    return null
}

この関数はString型を返すことになっているため、 nullを返すことはできません。

nullを返す可能性がある場合は、 戻り値をnullable型にします。

fun getName(): String? {
    return null
}

nullを返す必要がない場合は、 String型の値を返すように処理を修正します。

原因5:初期値としてnullを使用している

後から値を設定する予定の変数に、 とりあえずnullを入れて初期化しようとすると、 non-null型ではエラーになります。

var userName: String = null

userName = "Taro"

nullを初期値として使用するのであれば、 nullable型にします。

var userName: String? = null

userName = "Taro"

ただし、後から必ず値を設定するプロパティの場合は、 nullable型以外の方法が適していることもあります。

lateinitを使用する方法

クラスのプロパティなどで、 初期化時点では値を指定できないものの、 使用する前には必ず値を設定する場合、 lateinitを使用できることがあります。

lateinit var userName: String

fun setup() {
    userName = "Taro"
}

この方法では、 nullを初期値として指定する必要がありません。

lateinitで宣言したプロパティを、 値が設定される前に使用すると例外が発生します。 使用する前に確実に初期化される場合に利用します。

原因6:nullable型の値をnon-null型へ代入している

直接nullを書いていなくても、 nullになる可能性のある値をnon-null型へ代入すると、 型の不一致によるエラーが発生します。

val text: String? = null
val message: String = text

textString?なので、 nullである可能性があります。

そのため、 nullを許可しないStringへ そのまま代入することはできません。

Elvis演算子で初期値を指定する

nullable型の値がnullだった場合に、 別の値を使用したい場合は、 Elvis演算子 ?:を使用できます。

val text: String? = null
val message: String = text ?: "Default"

この場合、 textがnullでなければその値が使用され、 nullであれば "Default"が使用されます。

そのため、 messageには必ずString型の値が入ります。

nullチェックを行う

nullable型の値を使用する場合は、 nullでないことを確認してから処理する方法があります。

val text: String? = "Hello"

if (text != null) {
    println(text.length)
}

text != nullが成立する範囲では、 Kotlinがtextをnullではない値として扱える場合があります。

セーフコール演算子「?.」を使用する

nullable型のオブジェクトのプロパティや関数へアクセスするときは、 セーフコール演算子 ?.を使用できます。

val text: String? = null

println(text?.length)

textがnullの場合は、 lengthへアクセスせず、 式全体の結果がnullになります。

nullでなければ、 通常どおりlengthが取得されます。

「!!」で強制的にnon-nullとして扱う場合

Kotlinには、 nullable型の値を強制的にnon-nullとして扱う !!演算子があります。

val text: String? = "Hello"
val message: String = text!!

このコードでは、 textを強制的にString型として扱います。

ただし、実際の値がnullだった場合は、 実行時に例外が発生します。

!!はコンパイルエラーを簡単に回避できる場合がありますが、 nullだった場合の安全性は保証されません。 nullチェック、セーフコール、Elvis演算子などで処理できないかを先に検討します。

Android開発で発生する場合

Androidアプリでは、 Intent、Bundle、View、APIから取得したデータなど、 nullになる可能性のある値を扱う場面が多くあります。

例えば、Intentから文字列を取得する処理では、 戻り値がnullable型になる場合があります。

val name: String? = intent.getStringExtra("name")

この値をnon-null型へそのまま代入しようとすると、 nullの可能性を考慮する必要があります。

val name: String = intent.getStringExtra("name") ?: ""

この例では、 値を取得できなかった場合に空文字を使用しています。

Javaのコードやライブラリから値を受け取る場合

KotlinからJavaのコードやJavaで作成されたライブラリを使用する場合は、 nullの扱いに注意が必要です。

JavaではKotlinほど型レベルでnullの可否が明確に区別されていないため、 Java側から受け取った値がnullになる可能性があります。

外部のAPIやライブラリから取得した値を使用するときは、 戻り値の仕様を確認し、 必要に応じてnullチェックを行います。

すべてをnullable型にすればよいわけではない

「Null can not be a value of a non-null type」が表示された場合、 型の後ろに ?を付ければエラーを解消できることがあります。

var name: String? = null

しかし、 すべての変数をnullable型にすると、 その値を使用するたびにnullを考慮する必要があります。

本来必ず値が存在するものはnon-null型のままにし、 nullという状態が実際に必要なものだけをnullable型にする方が、 コードの意図を明確にできます。

エラーを消すためだけに?を追加するのではなく、 その変数に「値が存在しない状態」が必要なのかを考えて型を決めます。

「Null can not be a value of a non-null type」が出たときの確認順序

原因が分からない場合は、 次の順番で確認すると切り分けやすくなります。

  1. nullを代入している変数の型を確認する
  2. 型の後ろに?が付いているか確認する
  3. その変数が本当にnullを必要とするか確認する
  4. 関数の引数や戻り値の型を確認する
  5. nullable型の値をnon-null型へ代入していないか確認する
  6. Elvis演算子で初期値を指定できるか確認する
  7. nullチェックやセーフコールで処理できるか確認する
  8. 後から初期化するプロパティならlateinitが適切か確認する
  9. !!で無理に回避していないか確認する
  10. Android APIや外部ライブラリの戻り値がnullableか確認する

具体的なエラー箇所を確認する

「Null can not be a value of a non-null type」を解決するときは、 nullを指定している場所だけではなく、 その値がどのような型として定義されているかを確認することが重要です。

例:

val name: String = null
→ nullが必要ならString?に変更

fun getName(): String {
    return null
}
→ nullを返すなら戻り値をString?に変更

val text: String? = null
val message: String = text
→ nullチェックまたはElvis演算子を検討

val name: String = intent.getStringExtra("name")
→ 戻り値がnullになる可能性を確認

まとめ

Kotlinの 「Null can not be a value of a non-null type」は、 nullを許可していない型にnullを指定した場合に発生するエラーです。

Kotlinでは、 StringString?のように、 nullを許可する型と許可しない型が明確に区別されています。

エラーが表示された場合は、 まず対象となる変数、関数の引数、戻り値などが nullable型なのかnon-null型なのかを確認します。

nullという状態が必要であれば ?を付けてnullable型にし、 nullが不要であれば適切な初期値や値を設定します。

また、nullable型から値を取得する場合は、 nullチェック、 セーフコール演算子?.、 Elvis演算子?:などを利用することで、 nullを考慮した安全な処理を記述できます。

単にエラーを消すためにすべてをnullable型にしたり、 !!を多用したりするのではなく、 その値が本当にnullになる可能性があるのかを確認して、 適切な型を選択することが重要です。