※下記URLのサイトを参考にしました。英語、または技術的な知識が至らず、内容に誤りが含まれるおそれがありますので、ご了承ください。
※本エントリは参考サイトの翻訳をベースに、加筆・変更を施した構成となっています。
参考サイト http://confluence.jetbrains.net/display/Kotlin/Null-safety
null許容型と非null型
Javaプログラミングにおいて最も一般的な落とし穴のひとつは、NullPointerExceptionとなるようなnull参照のメンバにアクセスすることです。なぜなら、Javaプログラムでは実質的に、どの参照もnullを持つことができるからです。私たちがカジュアルな略称(NPE)で呼ぶそれは、非常によく起こります。
Kotlinの型システムには、私たちのコードからNPEを排除する狙いがあります。NPEの考えられる原因は次の3つだけです。
Kotlinの型システムは、nullを保持できる参照(null許容参照)と、できない参照(非null参照)を識別します。例えば、String型の通常の変数はnullを保持できません。nullを保持するためには、String? と書いて表現される null許容 Stringとして変数を宣言する必要があります。
var a : String = "abc" //a = null // nullは代入できない val l = a.length()
var b : String? = "abc" b = null val l = b.length() // コンパイル通らない
aのメソッドを呼び出してもNPEが起こらないことが保証されています。しかし bのメソッド呼び出しは安全ではないのでコンパイラはエラーを報告します。
条件におけるnullチェック
bがnullであるか明示的にチェックし、2つの選択肢を別々に扱えます。
val l = if (b != null) b.length() else -1http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
コンパイラはnullチェックの情報を追跡するので、if内でlength()を呼び出すことを許可します。より複雑な条件もサポートされています。
if (b != null && b.length() > 0) print("String of length ${b.length()}") else print("Empty string")http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
注意:これは、bがイミュータブルなところでのみ使用可能です。そうでない場合には、チェックの後でbがnullに変わるかも知れないからです。
安全呼び出し
null許容型のメンバにアクセスする2つ目の方法は、安全呼び出し(safe call)演算子で、?.と書きます。
b?.length()http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
bがnullでないならb.length()を、そうでないならnullを返します。この式の型はInt?です。
安全呼び出しは、チェーンにおいて便利です。
bob?.department?.head?.namehttp://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
このような呼び出しチェーンは、いずれかのプロパティがnullならnullを返します。
エルビス演算子
null許容型のrがあるとき「rがnullでないならばそれを使い、そうでないなら非null値のxを使用する」と言うことができます。
val l : Int = if (b != null) b.length() else -1http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
エルビス演算子(?:)で表現することもできます。
val l = b?.length() ?: -1http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
?:の左側の式がnullでないなら、エルビス演算子はそれ(左側の式)を、そうでないなら右側の式を返します。右辺の式は、左辺の式がnullのときのみ評価されることに注意してください。
sure()関数
3つ目の選択肢は、ぬるぽ愛好家のためにあります。b.sure()と書くと、これは非null型のbを返しますが、bがnullの場合はNPEを投げます。このように、望むならNPEを使えますが、明示的に問題を招く必要があり、それは突然に現れるものではありません。
ところで、sure()は特別な言語構造ではなく、標準ライブラリの拡張関数で、次のように定義されています。
inline fun <T : Any> T?.sure() : T = if (this == null) throw NullPointerException() else thishttp://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
述語演算子
Kotlinでは、 a?b() と書くことができます(b()は aのメソッド)。これを述語演算子(predicate operator)と呼び、次のように動作します。
if (a != null && a.b()) a else nullhttp://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
複数の述語を?マークで区切ることができます。
request.transaction?isInitialized()?isValid()?isReady()http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
これは、request.transactionがnullでなく、かつ3つの述語がすべてtrueなら、request.transactionを返します。
安全なキャスト
通常のキャストは、そのオブジェクトがターゲットの型でない場合、ClassCastExceptionを起こすことがあります。もう一つの選択肢は、安全なキャストを使うことです。これは、キャストに失敗するときには null を返します。
val aInt : Int? = a as? Inthttp://confluence.jetbrains.net/display/Kotlin/Null-safety より引用
この機能に関するベストプラクティス
J. Bloch, Effective Java. Second Edition
項目38:Check parameters for validity*2
項目43:Return empty arrays or collections, not nulls*3
Null Object Design Pattern
(元の提案はB. Woolfによる)