Safety)に設計された言語になっています。どのようにNull安全が保証されているのかについてまとめました。記事の途中にKotlinのNull安全がどれほどのものなのかの検証、実験を行なっています。先にその検証を読みたい方はここからどうぞ。それではKotlinのNull安全について見ていきましょう!参考にしたページは以下になります。SafetyKotlinのNull安全とはどういうものなのかKotlinのNull安全は、プログラムの実行時にNullPointerExceptionが発生する可能性を極力排除されるように設計されています。KitlinではNull参照に関する構文が厳しく設定されており、それらが守られていない場合、コンパイルエラーになるようになっています。このNull参照に関する構文の厳しさにより、NullPointerExceptionが発生する可能性を排除しています。そのため、KotlinはNull安全になります。どのようにNull安全が保証されているのかKotlinのNull安全はいくつかの方法で保証されています。その方法を実際のコードを交えながら説明していきたいと思います。非Null型とNull可能型の使い分けでNull安全が保証されるKotlinの型体系には、非Null型とNull可能型があります。非Null型はnull参照を保持することができません。Null可能型はnull参照を保持することができます。null参照を保持できない型とnull参照を保持できる型を区別することにより、KotlinはNull安全を保証しています。非Null型はどういった型なのでしょうか?それは、よく僕たちが見かける型が非null型になります。String、Int、Boolean、Person(自分で作ったクラス)などが非Null型になります。それではNull可能型とはどういった型なのでしょうか?各型に?をつけることでNull可能型となります。String?、Int?、Boolean?、Person?などのようにすることでnull参照可能な型となります。それでは実際にどのようにnull安全が保障されるのかを見ていきましょう。通常のString型(非Null型)はnullを保持できないため、以下のようにnullを保持しようとするとコンパイルエラーになります。//ここでコンパイルエラーnullを許可するためにはString?と記述し、Null可能型として宣言する必要があります。以下のようにString?で宣言した場合、nullを保持することができます。//コンパイルエラーにならないコードを見て気づかれている方も多いかと思いますが、普通に変数を宣言する場合、?をつけないので非Null型の宣言となります。意識せずに宣言した変数は必ずnullが保持できないことになるので、普通に宣言した変数のプロパティやメソッドなどにアクセスする際にNullPointerExceptionが発生しないことを保証します。これは思いもよらないnullの参照を防ぎ、思いもよらないバグを防ぎます。nonNullString.lengthここまで読んだ方はこう思われるかもしれません。Null可能型でnullを保持させて、その変数のプロパティやメソッドを呼び出したら結局NullPointerExceptionが発生するじゃん、と。Kotlinではそこも上手くNull安全が保証されています。Null可能型で普通にプロパティやメソッドを呼び出そうとするとコンパイルエラーになるようになっています。nullableString.lengthnullableStringはNull可能型で、nullの可能性があります。安全ではないため、コンパイラはエラーを発生させます。どのようにすればコンパイルエラーを発生させずに使用することができるのでしょうか?いくつかあるNull可能型の使い方を見ていきましょう。–Null可能型は、条件式でnullの確認を行うことでコンパイルエラーを発生させずに使用することができます。-1nullではない場合、nullableString.lengthの値が代入されます。この例のように、変数nullableStringがnullだった場合、任意の値を代入することもできます。コンパイラはNull可能型の確認についての情報を解析し、ifの中でのlengthへの呼び出しを許可するそうです。複雑な条件式にも対応しています。string")}上記の例ではifの条件で先にnullの確認を行なっているため、次の条件でlengthの呼び出しが許可されます。プロパティやメソッドの呼び出しだけでなく、Null可能なInt型の場合もnullの確認が必要になります。-1この使用方法は以下の場合にのみ動作することに注意してください、とKotlinの公式リファレンスに記載がありました。・バッキングフィールドを持ち、上書き不可能なvalプロパティなぜならば、他の場合、null確認をした後、nullに変更されることが発生する可能性があるからです、とも記載がありました。書いている意味はわかったのですが、本当にそうなのか、実際に検証してみました。ローカル変数でnull確認後、値を代入し使用してみるnull確認後、ローカル変数にnullを代入してみました。${nullableString.length}")}結果はコンパイルエラーになります。一つ目の条件の「nullの確認をしてから使用するまでの間に更新されていないローカル変数」に当てはまっていないからでしょうか?次にnull確認後、ローカル変数にnullでない値を代入してみました。${nullableString.length}")}これも「nullの確認をしてから使用するまでの間に更新されていないローカル変数」に当てはまっていないように思えます。結果はコンパイル成功でした。プログラムを実行したところ、正常に動作しました。それでは、null確認後、ローカル変数にnullでない値を代入し、更にnullでない値を代入してみました。${nullableString.length}")}同じく、コンパイル成功でプログラムも動作します。コンパイル時に、変数nullableStringに割り当てられた”update”は使われることがないですよ、という警告が表示されました。最後に、null確認後、ローカル変数にnullでない値を代入し、nullを代入してみました。${nullableString.length}")}まぁ半分予想がついていましたが、結果はコンパイルエラーでした。Null可能型のローカル変数を条件式の確認後に使用する場合、確認後にnullが代入されていなければコンパイルエラーにならず、プログラムは正常に動作することがわかりました。プロパティでnull確認後、値を代入し使用してみるそれではクラスに定義されたプロパティを使用して検証してみたいと思います。valで定義されたプロパティにnullを代入してみました。}}上記の結果はコンパイルエラーです。nullablePropertyにnullを代入しようとした時点でコンパイルエラーになります。これはNull安全の機能とは異なり、val(上書き不可能なプロパティ)で宣言されたプロパティに新たに値を代入しようとしたことによってのコンパイルエラーになります。話がそれますが、Kotlinはイミュータビリティに特化した言語で上記のようにvalで宣言したオブジェクトに対しての再代入を許可していません。詳しくはここを参照してください。実は上記のソースコードをコンパイルするともう一つコンパイルエラーが発生します。新たな値(null)の代入はvalなので許可されてませんが、nullが代入されたとしてコンパイラが判断し、nullが代入されたオブジェクトを参照しようとしてますよ、と親切にも文字列の長さを参照している箇所でコンパイルエラーを発生させます。厳し目なコンパイラですね。話がそれますが、Kotlinのクラスのプロパティは、ただプロパティの宣言をするだけで自動的にバッキングフィールドが作成されるようになっています。セッターとゲッターも定義しなくても自動で作成してくれます。逆に、Kotlinでは自分で作成したフィールドを持つことはできませんので注意してください。その代わり、自分で定義したセッター、ゲッター内でfield識別子を使用して自動で作成されたバッキングフィールドにアクセスできますし、プロパティをバッキングプロパティとしてセッター、ゲッター内で使用することができます。詳しくはここに記載があります。(英文のため、後にまとめて記事にする予定です)ちなみにバッキングフィールドとは、プロパティを通してアクセスできるprivteなフィールドのことを指します。ちなみのちなみに、Kotlinのクラスのインスタンスの生成にはnew修飾子の記述は必要ないです。いちいちnewを書かなくていいのでスマート!それでは次は、上書き可能なプロパティで検証してみましょう。上書き可能なプロパティにnullを代入してみました。}}結果はコンパイルエラーでした。これは二つ目の条件「バッキングフィールドを持ち、上書き不可能なvalプロパティ」に当てはまらないためでしょうか。コンパイルエラーの内容はnullを参照しようとしていますよ、というエラーでした。次は上書き可能なプロパティにnullでない値を代入してみました。}}結果はコンパイルエラーでした。ローカル変数と同じ結果になるかと思っていましたが、予想と反してコンパイルエラーになりました。コンパイルエラーの内容は、ミュータブル(変更可能)なオブジェクトをこの方法では参照できません、というエラーでした。二つ目の条件「バッキングフィールドを持ち、上書き不可能なvalプロパティ」に当てはまらないため、コンパイルエラーになったということがわかりますね。この結果から、クラスにあるミュータブルなオブジェクトを参照する場合、条件式を使った方法では、Null可能型のオブジェクトは参照できないことがわかりました。Kotlinの公式のリファレンスのとおり、条件式を使ったNull可能型のオブジェクトへの参照には、Null安全が保証されているようです。他にも、検証してみました。Javaで作成したクラスでの検証KotlinはJavaとの相互運用ができますのでJavaのクラスを使った検証を行いたいと思います。先ほどKotlinで使用していたクラスに相当するクラスをJavaに定義し、そのクラスのプロパティを条件式を使わずに使用するとどうなるのか試してみます。${length}")}結果はコンパイル成功でした。実行したところ、エラーが発生することなくプログラムが動作しました。今回のように、Javaから呼び出すクラスにString型のようなnull参照できる型がプロパティに含まれている場合、KotlinのNull安全は機能しません。Javaと同じようにプロパティを条件式でnull確認することなく、String型のような参照型を使用することができます。その代わり、NullPointerExceptionもJavaで動作させるのと同じように発生します。要はJavaと同じ動作になるということですね。以下のようにすると実行時にNullPointerExceptionが発生します。nullablePropertyHolder.nullableProperty.lengthKotlinでは、Javaで定義されたクラス(String型なども含む)はプラットフォーム型と呼んでいるようです。全てのプラットフォーム型は、KotlinのNull安全の機能が働かなく、Javaと同じようにNullPointerExceptionが発生します。NullablePropertyHolderにStringの代わりにJavaで定義されたクラスをプロパティとして持っていた場合、そのプロパティはnullチェックすることなく使用することができ、null参照するとNullPointerExceptionが発生します。詳しくはここに記載があります。(英文のため、後に記事にまとめる予定です)ですが、プラットフォーム型であっても、Kotlinのコード内で非Null型として宣言して使用する場合、その参照自体にはnullを代入することはできません。null話はそれますが、ここに記載しているプログラムは全てコマンドラインでコンパイルし実行しています。Overflowで質問してしまいました。Javaのjarクラスを含んだKotlinのプログラムをコマンドラインで動かす方法が全然見つからなかったんだもん(笑かいつまんでいうと、Kotlinが参照しているJavaのクラスを呼び出そうとするとクラスが見つからないエラーが出てしまうという現象に陥りました。Overflowにて親切なナイスガイの回答を読んでわかったのですが、Javaでいうところの、実行するjarが依存しているjarは全てクラスパスに渡す必要がある、ということでした。Kotlinでも同じで、依存しているjarを読み込まないとクラスが見つからないということですね。Javaのjarをコマンドラインで動かしたことなかったので勉強になりました。後日、このことをまとめて記事にしたい思っています。Overflowでの僕の質問はここになります。Kotlinで作成したクラスにあるミュータブルでNull可能なプロパティにJavaでnullを代入して返すメソッドを作り、Kotlinでnullの入ったプロパティを使う上記のタイトルのとおり、検証したいと思います。${length}")}結果はコンパイルエラーでした。Kotlinで定義したクラスを使用しているため、いくらJavaでnullを代入しようが、Null安全が働き、コンパイル時に、Null参照している可能性があるオブジェクトを使おうとしている、といったエラーが発生します。条件式でnull確認を行ってみましたが、こちらもコンパイルエラーになりました。-1これは二つ目の条件「バッキングフィールドを持ち、上書き不可能なvalプロパティ」に当てはまらないため、条件式でのnull確認をしてもコンパイルエラーになるということでした。Javaで定義したgetNullablePropertyHolderWithNullInProperty()をKotlinで使用し、型指定をしなかった場合、デフォルトではKotlinで定義したNullablePropertyHolderクラスの非Null型として推測され、参照を受け取ります。Java側のgetNullablePropertyHolderWithNullInProperty()でNullablePropertyHolderクラスをnullで返した場合、NullablePropertyHolderのインスタンスを後述する?.演算子を使って参照した場合、IllegalStateExceptionが発生します。-1Kotlinで作成したクラスにあるミュータブルで非NullなプロパティにJavaでnullを代入して返すメソッドを作り、Kotlinでnullの入ったプロパティを使う次の検証は、非Nullなオブジェクトとして定義されたプロパティにJava側でnullを代入し、そのプロパティを使用してみます。どんな結果になるのでしょうね。${length}")}まずコンパイルはエラーになることなく、成功しました。Kotlinで定義しているNonNullPropertyHolderクラスのnonNullPropertyは非Null型のため、そもそもコンパイラはKotlinのソースコードでnull参照が行われていないと判断します。今回のようにJava側で非Null型のオブジェクトにnullを代入されていようが、Kotlinのコンパイラはそれを感知しません。実行したところ、以下のようなエラーが発生しました。kotlinsorce.CallGetSetNonNullPropertyHolderKt.main(CallGetSetNonNullPropertyHolder.kt:8)Kotlinで定義した非Null型のオブジェクトにJavaでnullを代入した場合、Javaコードのために自動生成されたセッター内でIllegalArgumentExceptionが発生するようになっているみたいですね。Kotlinで定義した非Null型のオブジェクトにJavaでnullを代入した場合、コンパイル時点ではnullの代入の検出はできませんが、実行時にnullの代入を検出できるようになっているようです。この動作からもKotlinのNull安全が働いているということがわかりますね。詳しいことはここに記載があります。(英文のため、後に記事にまとめる予定です)このように、実行時にNullPointerExceptionの代わりにIllegalArgumentExceptionが発生してしまい、ランタイムエラーとなってしまいますが、Kotlinに定義されている非Null型のプロパティにnullを代入しないようにJavaのコードを修正する必要が出てくるため、思わぬNull参照は避けられるようになっています。この修正は一見面倒なように思えますが、後に大きな財産になる修正になると僕は思います。KotlinのNull安全は伊達ではないですね!引き続き、KotlinのNull安全に関する解説をしていきます。条件式を使用せずにNull可能型のオブジェクトを参照するには?.演算子を使用します。?.演算子を使用することで安全にNull可能型のオブジェクトを参照することができます。nullableString?.lengthNull可能型のオブジェクトのメンバーなどにアクセスしたい場合に上記のように書くことで安全に参照することができます。Null可能型のオブジェクト、ここではnullableStringがnullではない場合、lengthプロパティの値が返ってきます。nullableStringがnullの場合、nullを返します。?.演算子を使用する場合、以下のように処理することができます。exist.")}?.演算子を使った処理は後述するエルビス演算子を使用することでより簡潔に書くことができます。この?.演算子は、先ほど説明したプラットフォーム型のプロパティを同じ方法で安全に参照することもできます。プラットフォーム型を使用する時は積極的に使用したほうがよさそうですね!また、?.演算子はチェーンを使用する場合にも役に立ちます。あるファイル管理システムがあったとします。そのファイル管理システムではフォルダにタグの設定ができます。そこで、あるタグが設定されたフォルダのみのリストを取得する処理を書きたいとします。フォルダにはフォルダ情報が含まれており、その中にタグの情報が含まれており、タグ情報にはタグの重要度やタグの色、タグの名前が含まれているとします。そのような処理をしたい場合、以下のように処理することができます。specifiedTagFolderList.add(currentFolder)}この処理では、フォルダに含まれるフォルダ情報、フォルダ情報に含まれるタグ情報、タグ情報に含まれるタグ名の順に参照していき、どこかがnullなら変数tagNameにはnullが代入され、タグ名まで全てが存在した場合、タグ名が変数に代入されるようになります。このように、チェーンを使用する場合、この?.演算子はとても役に立つものとなっています。nullでない値の場合のみチェーン操作を実行したい場合、let関数を使用することで安全に処理を行うこともできます。}}let関数は渡されたラムダ式をただ単に実行する関数です。この処理では?.演算子で各プロパティをチェーンで繋ぎ、最後のプロパティのtagNameがnullでない場合にlet関数を実行するようにしています。することにより、nullでないオブジェクトへの使用を安全に行うことができます。letが実行された時点でtagNameがnullでないことが確定しているため、null確認もしなくてもいいというのも利点の一つです。この処理で出てきたit演算子ですが、it演算子はラムダ式内で使用できる演算子です。let内のit演算子はtagNameの参照がitとなっています。it演算子に関してはこことここに記載があります。–エルビス演算子を使用することで、条件式でNull可能型を使用できなかったオブジェクトを安全に参照することができます。エルビス演算子を使用すると、もしそのオブジェクトがnullでなければそのオブジェクトを使用し、もしnullなら非Null型の任意の値を返すような記述をすることができます。-1?:がエルビス演算子になります。エルビス演算子は以下の処理の省略系に近いです。-1上記の処理は、条件式でnullを確認する、で説明したとおり、特定のNull可能型のオブジェクトの場合、使用することができません。エルビス演算子と?.演算子を使用することで、全てのNull可能型のオブジェクトを安全に参照することができます。エルビス演算子は、エルビス演算子(?:)の左側がnullでない場合、左側のオブジェクトを返します。nullの場合、右側の式を返します。条件式でいうifとelseの関係になりますので、式の右側は、左側がnullの場合のみ評価されることに注意してください。エルビス演算子を使用した式では、throwやreturnをエルビス演算子の右側で使用することもできます。関数の引数の確認で有用になる可能性があります。IllegalArgumentException("名前が必要です。俺はNullPointerExceptionを発生させたいんだ!そんなNullPointerExceptionを愛してやまない人にうってつけの演算子がKotlinには用意されています。それが!!演算子です。nullableString!!.length!!演算子は、もしNull可能型のオブジェクトがnullでなければそのまま値を返し、もしnullならNullPointerExceptionをthrowします。NullPointerExceptionを発生させたいなら、!!演算子を使用することでNullPointerExceptionを拾うことができます。Null安全でプログラムを組めるといっても、NullPointerExceptionを拾いたい場面はあります。そのような場合に使用する演算子になります。Null可能型を使用した安全なキャスト標準のキャストは、そのオブジェクトが目的の型でない場合、ClassCastExceptionを発生させます。標準のキャストの代わりに、as?演算子を使用して安全にキャストをすることができます。Intas?演算子を使用すると、対象のオブジェクトが目的の型にキャストできなかった場合、nullを返します。as?演算子の機能は、C#でいうas演算子を使用したキャストと同じような機能です。使いどころはたくさんあると思います。Null可能型のコレクションを安全に使用するNull可能型の要素を持つコレクションを使用していて非Nullな要素のみを取り出したい場合、filterNotNull関数を使用することによってそれを実現することができます。nullableList.filterNotNull()使いどころとしては、JavaでListを取得するような処理の後にfilterNotNull関数を呼び出すことによって、要素がnullかどうかを気にすることなく使用できます。上記で紹介した機能の他にNull安全な機能がKotlinには備わっています。Kotlinでは関数の引数にもNull可能型、非Null型を指定する必要があります。以下の関数は必ずnullでないオブジェクトが来るのでnull確認することなく処理を行えます。target2.length}非Null型の引数のため、必ずnullではないため、安全に参照することができます。次にプラットフォーム型を使用する時の安全な使い方です。Javaで定義されたメソッドで返り値として設定されたプラットフォーム型はnullで返ってくる可能性があります。そこで以下のように返り値を受け取ることで、受け取ったオブジェクトの参照に関してはNull安全になります。Null可能型のオブジェクトとして明示的に宣言しているため、KotlinのNull安全に則った呼び出しをしていないのでここでコンパイルエラーになるnullableHogeClass.bar()このように、Javaのメソッドから受け取るオブジェクトは明示的にNull可能型として宣言して受け取れば、そのオブジェクトの参照はNull安全になります。nullが返ってくるようなメソッドを使用する場合、明示的に宣言することをおすすめします。そのオブジェクトが持つプロパティに関してはKotlinのNull安全が働かないので注意してください。Javaのクラスのプロパティにアクセスする際は、?.演算子を使用することをおすすめします。することにより、KotlinのNull安全が働きます。KotlinのNull安全はすごい、だけど...このように非Null型とNull可能型の使い分けによってKotlinではNull安全が保証されます。nullの参照の可能性をコンパイル時点でエラーとして報告してくれることは、開発していく上で、この上なく効率がいいです。無駄なデバッグのコストも削減できるでしょう。それでは、KotlinのNull安全は万能なのでしょうか。これまでのKotlinのNull安全の機能を見直すと万能のようにも感じます。KotlinのNull安全は完全に安全というわけではありません。それではどのような場合、Null安全でなくなるのかを見ていきましょう。KotlinでNullPointerExceptionが発生する場面KotlinでNullPointerExceptionが発生する可能性は以下になります。NullPointerException()をthrowする明示的な呼び出し先ほど説明した!!演算子を使用することでNullPointerExceptionが明示的に呼び出され、NullPointerExceptionが発生します。!!演算子を使うということは、どこかでエラーをcatchすると思いますので、そこまで深刻なNullPointerExceptionの発生ではないですね。外部JavaコードでのNullPointerExceptionの発生これに関してはKotlin側からできることは少ないです。できることといえば、Java側で定義されたメソッドの仕様を守り、引数に渡すオブジェクトができる限りnullにならないようにすることです。Javaのメソッドの引数がStringなどの型の場合は、Kotlin側で非Null型として宣言すればNullPointerExceptionを避けることはできそうですね。初期化に関してのデータの不一致がある(コンストラクター内のどこかで、利用可能な初期化されていないオブジェクトが使用される)この説明だけだとわかりにくいですよね。長いですが、以下にその例を記載します。簡単に言うと、以下の処理ではコンストラクタ内で未初期化のStringのlengthプロパティを参照しようとし、NullPointerExceptionが発生してしまいます。FoolishNullReferenceClass()}Kotlinのクラスのプロパティは、基本的にはコンストラクタの処理が終了するまでに初期化する必要があります。コンストラクタの処理が終了するまでに初期化しなかった場合、コンパイルエラーになります。上記の処理ではコンストラクタ内できちんとプロパティを初期化しています。その1行前でprintLengthOfString関数が呼び出されており、関数の中では初期化する前のStringの長さを参照しています。未初期化なため、値が入っていなく、NullPointerExceptionが発生します。このパターンは起きなさそうで起きそうなパターンだと思いますので、このようなミスをしないように気をつけたいですね。昨今、プログラミングのスキルを求められる機会はだんだんと増えてきました。そのため、プログラミングのスキルを身につけることは自分の人生をがらっと変えてしまえるくらいの影響力を持ちます。あなたがプログラミングを基礎からきっちりと習いたいと考えているなら、『CodeCamp』というおすすめのオンラインプログラミングスクールがあります。詳細を以下の記事にまとめています。プログラミングスキルを適切に身につけ、ぜひ、あなたのこれからの人生を変貌させましょう!プログラミング学習を考えている人に勧めたいオンラインスクールCodeCampまとめかなり長くなってしまいましたが、これがKotlinのNull安全の全てです。このようにKotlinのプログラム内では、NullPointerExceptionがほぼ発生しないような設計がされています。コンパイル時点でnull参照のルールに引っかかると、コンパイラがエラーを発生させるのはとても魅力的な機能だと思いました。Kotlinで開発すると思いもよらないnull参照のエラーはほぼ防がれることかと思います。この機能だけでも、Kotlinを使用して開発するメリットがあると感じました。今回はKotlinのNull安全についてまとめましたが、Kotlinのメリットについてまとめた記事もあります。よろしければ合わせてご参照ください。コメントをキャンセルメールアドレスが公開されることはありません。