1. 始める前に
この Codelab では、null 可能性と、null
安全の重要性について学習します。null 可能性は、多くのプログラミング言語で一般的に見られる概念です。これは、値を持たないことが可能であるという、変数の能力のことです。Kotlin では、null
安全を達成するために、null 可能性を意識的に取り扱っています。
前提条件
- Kotlin プログラミングの基礎知識(変数を含む)、変数からメソッドやプロパティへのアクセスに関する知識、
println()
関数とmain()
関数の知識 if/else
文やブール式などの Kotlin の条件構文に慣れていること
学習内容
null
とは何か- null 値許容型と null 値非許容型の違い
null
安全とその重要性、Kotlin がどのようにnull
安全を実現するか- セーフコール演算子(
?.
)と非 null 値アサーション演算子(!!
)を使用して null 許容変数のメソッドとプロパティにアクセスする方法 if/else
条件構文を使用してnull
チェックを行う方法if/else
式を使用して null 値許容変数を null 値非許容型に変換する方法- null 値許容変数が
null
の場合のデフォルト値を、if/else
式またはエルビス演算子(?:
)で指定する方法
必要なもの
- Kotlin プレイグラウンドにアクセスできるウェブブラウザ
2. null 値許容変数を使用する
null
とは何か
ユニット 1 では、変数を宣言する場合、すぐに値を代入する必要があることを学習しました。たとえば、favoriteActor
変数を宣言する場合に、すぐに "Sandra Oh"
という文字列値を代入します。
val favoriteActor = "Sandra Oh"
では、お気に入りの俳優がいない場合はどうでしょうか。この変数に "Nobody"
や "None"
という値を代入することもできます。しかし、プログラムは favoriteActor
変数に値がないとは解釈せず、"Nobody"
や "None"
という値があると解釈するため、良い方法ではありません。Kotlin では、null
を使用することで、変数に関連付けられた値がないことを示すことができます。
以下のようにして、コードで null
を使用しましょう。
- Kotlin のプレイグラウンドで、
main()
関数の本体の内容を、null
に設定されたfavoriteActor
変数に置き換えます。
fun main() {
val favoriteActor = null
}
favoriteActor
変数の値をprintln()
関数で出力するようにしてから、このプログラムを実行します。
fun main() {
val favoriteActor = null
println(favoriteActor)
}
出力は、次のコード スニペットのようになります。
null
変数に null
を再代入する
以前に、var
キーワードで定義された変数には、同じ型の異なる値を再代入できることを学習しました。たとえば、新しい俳優の名前が String
型である限り、ある名前で宣言されている name
変数に別の名前を再代入できます。
var favoriteActor: String = "Sandra Oh"
favoriteActor = "Meryl Streep"
変数を宣言した後で、その変数に null
を代入する必要が生じることがあります。たとえば、お気に入りの俳優を宣言した後で、それを一切公開しないことにした場合などです。この場合、favoriteActor
変数への null
の代入が使えます。
null 値非許容変数と null 値許容変数について
以下のようにして favoriteActor
変数に null
を再代入しましょう。
val
キーワードをvar
キーワードに変更し、favoriteActor
変数がString
型であることを指定して、お気に入りの俳優の名前を代入します。
fun main() {
var favoriteActor: String = "Sandra Oh"
println(favoriteActor)
}
println()
関数を削除します。
fun main() {
var favoriteActor: String = "Sandra Oh"
}
favoriteActor
変数にnull
を再代入するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String = "Sandra Oh"
favoriteActor = null
}
次のエラー メッセージが表示されます。
Kotlin では、null 値許容型と null 値非許容型の区別があります。
- null 値許容型は、
null
を保持できる変数です。 - null 値非許容型は、
null
を保持できない変数です。
型が null 値許容なのは、それに null
を明示的に保持させる場合のみです。エラー メッセージが示すように、String
データ型は null 値非許容型であるため、この変数に null
を再代入することはできません。
Kotlin で null 値許容変数を宣言するには、型の末尾に ?
演算子を追加する必要があります。たとえば、String?
型は文字列か null
のいずれかを保持できますが、String
型は文字列しか保持できません。null 値許容変数を宣言するには、null 値許容型を明示的に追加する必要があります。null 値許容型でない場合、Kotlin コンパイラは null 値非許容型だと推論します。
favoriteActor
変数の型をString
データ型からString?
データ型に変更します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
favoriteActor = null
}
null
の再代入の前後でfavoriteActor
変数を出力するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor)
favoriteActor = null
println(favoriteActor)
}
出力は、次のコード スニペットのようになります。
Sandra Oh null
favoriteActor
変数は、最初は文字列を保持していましたが、その後 null
に変わります。
試してみる
null 値許容の String?
型を使用できるようになったので、Int
値で変数を初期化してから、null
を再代入しましょう。
null 値許容の Int
値を記述する
main()
関数内のすべてのコードを削除します。
fun main() {
}
- null 値許容の
Int
型のnumber
変数を作成し、10
という値を代入します。
fun main() {
var number: Int? = 10
}
number
変数を出力するようにしてから、このプログラムを実行します。
fun main() {
var number: Int? = 10
println(number)
}
出力は想定どおりです。
10
number
変数が null 値許容であることを確かめるために、null
を再代入します。
fun main() {
var number: Int? = 10
println(number)
number = null
}
- プログラムの最後の行として別の
println(number)
文を追加し、実行します。
fun main() {
var number: Int? = 10
println(number)
number = null
println(number)
}
出力は想定どおりです。
10 null
3. null 値許容変数の扱い方
以前に、「.
」演算子を使用して null 値非許容変数のメソッドとプロパティにアクセスする方法を学習しました。このセクションでは、null 値許容変数のメソッドとプロパティにアクセスする方法について説明します。
以下の手順で、null 値非許容の favoriteActor
変数のプロパティにアクセスしましょう。
main()
関数のすべてのコードを削除し、String
型のfavoriteActor
変数を宣言して、お気に入りの俳優の名前を代入します。
fun main() {
var favoriteActor: String = "Sandra Oh"
}
length
プロパティを使ってfavoriteActor
変数値の文字数を出力するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String = "Sandra Oh"
println(favoriteActor.length)
}
出力は想定どおりです。
9
favoriteActor
変数にはスペースを含めて 9 文字が入っています。文字数はお気に入りの俳優の名前によって異なります。
null 値許容変数のプロパティにアクセスする
favoriteActor
変数を null 値許容にして、お気に入りの俳優がいない場合は、この変数に null
を代入できるようにすることを考えます。
以下の手順で、null 値許容の favoriteActor
変数のプロパティにアクセスしましょう。
favoriteActor
変数の型を null 値許容型に変更してから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor.length)
}
次のエラー メッセージが表示されます。
このエラーはコンパイル エラーです。前の Codelab で説明したように、コンパイル エラーは、コードの構文エラーが原因で Kotlin がコードをコンパイルできない場合に発生します。
Kotlin では、null
安全(null
の可能性がある変数での呼び出しが間違って行われないことが保証されているという意味)を達成できるように構文上のルールが意図的に適用されます。これは、変数を null
にできないという意味ではありません。変数のメンバーがアクセスされる場合には、その変数を null
にできないという意味です。
このことは、アプリの実行中に null
である変数のメンバーへのアクセス(null
参照)を行おうとすると、null
変数にはプロパティやメソッドがないためアプリがクラッシュすることから、大変重要です。この種のクラッシュは、コードをコンパイルして実行したあとで発生するエラーであることから、ランタイム エラーと呼ばれています。
Kotlin には null
安全という性質があるため、Kotlin コンパイラが null 値許容型に対する null
チェックを強制して、このようなランタイム エラーを防いでいます。Null
チェックとは、変数にアクセスして null 値非許容型として扱う前に、その変数が null
になる可能性をチェックする処理のことです。null 値許容型を null 値非許容型として使用する場合は、null
チェックを明示的に行う必要があります。詳しくは、この Codelab の後半にある if/else
条件構文の使用に関するセクションをご覧ください。
この例の場合、favoriteActor
変数の length
プロパティを直接参照できないことが原因でコンパイル時エラーとなります。これは、変数が null
になる可能性があるためです。
次は、null 値許容型を扱うさまざまな手法と演算子について学習します。
セーフコール演算子(?.
)を使用する
セーフコール演算子(?.
)使用して、null 値許容変数のメソッドやプロパティにアクセスできます。
セーフコール演算子(?.
)を使用してメソッドやプロパティにアクセスするには、変数名の後に「?
」を追加し、「.
」記法でメソッドやプロパティにアクセスします。
セーフコール演算子(?.
)を使用すると、Kotlin コンパイラが null
参照に対するメンバー アクセスをすべて阻止して、アクセスされたメンバーの代わりに null
を返すため、null 値許容変数に安全にアクセスできます。
以下の手順で、null 値許容の favoriteActor
変数のプロパティに安全にアクセスしましょう。
println()
文で、「.
」演算子をセーフコール演算子(?.
)に置き換えます。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor?.length)
}
- このプログラムを実行して、出力が予想どおりであることを確認します。
9
文字数はお気に入りの俳優の名前によって異なります。
favoriteActor
変数にnull
を再代入するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = null
println(favoriteActor?.length)
}
以下のような出力が表示されます。
null
null
変数の length
プロパティにアクセスしようとしましたが、プログラムはクラッシュしていません。セーフコール式は null
を返しています。
非 null アサーション演算子(!!
)を使用する
null 値許容変数のメソッドやプロパティにアクセスするために、非 null アサーション演算子(!!
)を使用することもできます。
null 値許容変数の後に、非 null アサーション演算子(!!
)を追加し、その後に「.
」演算子を追加して、さらにその後にメソッドまたはプロパティを追加する必要があります(スペースは入れません)。
その名前のとおり、非 null アサーション(!!
)を使用すると、変数の値がどちらであるかにかかわらず、null
でないことをアサートすることになります。
セーフコール演算子(?.
)とは異なり、非 null アサーション演算子(!!
)を使用すると、null 値許容変数が実際に null
の場合に NullPointerException
エラーがスローされます。したがって、変数が常に null 値非許容の場合か、適切な例外処理が設定されている場合にのみ行う必要があります。処理しない場合は、例外によりランタイム エラーが発生します。例外処理については、このコースの後半のユニットで説明します。
以下の手順で、非 null アサーション演算子(!!
)を使用して favoriteActor
変数のプロパティにアクセスしましょう。
favoriteActor
変数にお気に入りの俳優の名前を再代入し、println()
文のセーフコール演算子(?.
)を非 null アサーション演算子(!!
)に置き換えます。
fun main() {
var favoriteActor: String? = "Sandra Oh"
println(favoriteActor!!.length)
}
- このプログラムを実行して、出力が予想どおりであることを確認します。
9
文字数はお気に入りの俳優の名前によって異なります。
favoriteActor
変数にnull
を再代入するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = null
println(favoriteActor!!.length)
}
NullPointerException
エラーが発生します。
この Kotlin エラーは、プログラムが実行中にクラッシュしたことを示しています。このため、変数が null
でないことが確実な場合を除き、非 null アサーション演算子(!!
)の使用はおすすめしません。
if/else
条件構文を使用する
if/else
条件構文の if
分岐を使って、null
チェックを行うことができます。
null
チェックは、!=
比較演算子で null 値許容変数が null
と等しくないことを確認することで行うことができます。
if/else
文
次のように if/else
文を null
チェックと組み合わせて使用できます。
null チェックは、if/else
文と組み合わせると便利です。
nullableVariable != null
という式のnull
チェックをif
の条件として使用します。if
分岐の本体 1 では、変数が null 値非許容の値であると想定されます。したがって、この本体では null 値非許容変数であるかのように変数のメソッドやプロパティに自由にアクセスできます。セーフコール演算子(?.
)や非 null アサーション演算子(!!
)は不要です。else
分岐の本体 2 では、変数がnull
であると想定されます。したがって、この本体には、変数がnull
のときに実行する文を追加できます。else
分岐は省略可能です。null
チェックが失敗した場合のデフォルト動作を指定せずに、null
チェックを実行するif
条件構文のみを使用できます。
null 値許容変数を使用するコードが複数行ある場合は、if
条件で null
チェックを使用するのが便利です。これに対して、null 値許容変数の参照が 1 回の場合は、セーフコール演算子(?.
)が便利です。
以下の手順で、favoriteActor
変数の null
チェックを含む if/else
文を記述しましょう。
- 今回も
favoriteActor
変数にお気に入りの俳優の名前を代入し、println()
文を削除します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
}
favoriteActor != null
という条件のif
分岐を追加します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
}
}
if
分岐の本体に、"The number of characters in your favorite actor's name is ${favoriteActor.length}"
文字列を受け取るprintln
文を追加してから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
}
}
出力は想定どおりです。
The number of characters in your favorite actor's name is 9.
文字数はお気に入りの俳優の名前によって異なります。
null
チェックの後の if
分岐内で length
メソッドにアクセスするため、「.
」演算子を使用して名前の length メソッドに直接アクセスできています。このように、Kotlin コンパイラは favoriteActor
変数が null
になる可能性がないことを認識しているため、プロパティに直接アクセスすることを許しています。
- 省略可:
else
分岐を追加して、俳優の名前がnull
になる状況に対応します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
}
}
else
分岐の本体に、"You didn't input a name."
文字列を受け取るprintln
文を追加します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
favoriteActor
変数にnull
を代入するようにしてから、このプログラムを実行します。
fun main() {
var favoriteActor: String? = null
if(favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
出力は想定どおりです。
You didn't input a name.
if/else
式
null
チェックと if/else
式を組み合わせて、null 値許容変数を null 値非許容変数に変換することもできます。
次のようにして、null 値非許容型に if/else
式を代入します。
nullableVariable != null
というnull
チェックをif
条件として使用します。if
分岐の本体 1 では、変数が null 値非許容の値であると想定されます。したがって、この本体では null 値非許容変数であるかのように変数のメソッドやプロパティにアクセスできます。セーフコール演算子(?.
)や非 null アサーション演算子(!!
)は不要です。else
分岐の本体 2 では、変数がnull
であると想定されます。したがって、この本体には、変数がnull
のときに実行する文を追加できます。- 本体 1 と本体 2 の最後の行では、null 値非許容型になる式や値を使用し、それぞれ
null
チェックが成功したときと失敗したときに、それが null 値非許容変数に代入されるようにする必要があります。
以下の手順で、if/else
式を使用して、println
文を 1 つだけ使用するようにプログラムを書き換えましょう。
- お気に入りの俳優の名前を
favoriteActor
変数に代入します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
if (favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
lengthOfName
変数を作成してから、if/else
式を代入します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if(favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${favoriteActor.length}.")
} else {
println("You didn't input a name.")
}
}
if
分岐とelse
分岐の両方からprintln()
文を削除します。
fun main() {
var favoriteActor: String? = "Sandra Oh"
val lengthOfName = if(favoriteActor != null) {
} else {
}
}
if
分岐の本体に、favoriteActor.length
という式を追加します。
fun main() {
val favoriteActor: String? = "Sandra Oh"
val lengthOfName = if(favoriteActor != null) {
favoriteActor.length
} else {
}
}
favoriteActor
変数の length
プロパティには、「.
」演算子で直接アクセスします。
else
分岐の本体に、0
の値を追加します。
fun main() {
val favoriteActor: String? = "Sandra Oh"
val lengthOfName = if(favoriteActor != null) {
favoriteActor.length
} else {
0
}
}
0
の値は、名前が null
の場合のデフォルト値として使用されます。
main()
関数の最後に、"The number of characters in your favorite actor's name is $lengthOfName."
という文字列を受け取るprintln
文を追加し、次のプログラムを実行します。
fun main() {
val favoriteActor: String? = "Sandra Oh"
val lengthOfName = if(favoriteActor != null) {
favoriteActor.length
} else {
0
}
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
出力は想定どおりです。
The number of characters in your favorite actor's name is 9.
文字数は名前によって異なります。
エルビス演算子(?:
)を使用する
エルビス演算子(?:
)は、セーフコール演算子(?.
)と合わせて使用できる演算子です。エルビス演算子(?:
)を使用することで、セーフコール演算子(?.
)が null
を返したときのデフォルト値を追加できます。これは if/else
式に似ていますが、より Kotlin らしい書き方です。
変数が null
でない場合、エルビス演算子(?:
)の前の式が実行されます。変数が null
の場合、エルビス演算子(?:
)の後の式が実行されます。
以下の手順で、以前のプログラムをエルビス演算子(?:
)を使用するように変更しましょう。
if/else
条件構文を削除して、lengthOfName
変数に null 値許容のfavoriteActor
変数を設定し、セーフコール演算子(?.
)演算子を使用してlength
プロパティを呼び出します。
fun main() {
val favoriteActor: String? = "Sandra Oh"
val lengthOfName = favoriteActor?.length
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
length
プロパティの後で、エルビス演算子(?:
)の後に0
の値を追加してから、このプログラムを実行します。
fun main() {
val favoriteActor: String? = "Sandra Oh"
val lengthOfName = favoriteActor?.length ?: 0
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
出力は前の出力と同じです。
The number of characters in your favorite actor's name is 9.
4. まとめ
これで、null 可能性と、それをさまざまな演算子で扱う方法の学習が終わりました。
まとめ
- 変数に
null
を設定することで、値を保持していないことを示せます。 - null 値非許容変数には
null
を代入できません。 - null 値許容変数には
null
を代入できます。 - null 値許容変数のメソッドやプロパティにアクセスするには、セーフコール演算子(
?.
)か非 null アサーション演算子(!!
)を使用する必要があります。 null
チェックと合わせてif/else
文を使用すると、null 値非許容のコンテキストで null 値許容変数にアクセスできます。if/else
式を使用すると null 値許容変数を null 値非許容型に変換することができます。if/else
式またはエルビス演算子(?:
)を使用して、null 値許容変数がnull
の場合のデフォルト値を指定できます。