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() {
    var favoriteActor: String? = "Sandra Oh"
    val lengthOfName = if (favoriteActor != null) {
      favoriteActor.length
    } else {
      
    }
}
favoriteActor 変数の length プロパティには、「.」演算子で直接アクセスします。
- else分岐の本体に、- 0の値を追加します。
fun main() {
   var 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() {
    var 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() {
   var 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() {
    var 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の場合のデフォルト値を指定できます。
 
  