実際にサンプルコードで動作を確認してみよ。
例1:nullを許可しない変数
通常の変数宣言は、nullを許容しない変数となります。つまり、nullを代入することが出来ない変数なので、Javaの実装でよくあるnullだったらどうしよう・・・という分岐が必要なくなります。
例えば、以下のコードはnullを許容しない変数にたいするnull代入となるためコンパイルエラーとなります。
var s:String s = null
例2:nullを許可する変数
nullを許容する変数は、型の末尾に「?」を付加する必要があります。これにより、nullを変数に代入可能となります。また、その変数を参照する際には、変数の末尾に「?」を付加して参照しないとコンパイルエラーになります。
このnull許可変数は、参照した際にnullの場合はnullが返却されるようになっています。JavaのようにNullPointerExceptionが発生することはないわけですね。
// 型の末尾に「?」を付加する。 var s:String? = "hoge" val length = s?.length() // -> 4 // 変数がnullの場合に、参照を行うと戻り値もnullとなります。 s = null val length2 = s?.length() // -> null
例3:Javaと同じようにNullPointerExceptionを送出したい場合
sure関数を使用すると、nullの場合にNullPointerExceptionが送出されます。var s:String? = "1" var length = s.sure().length() // nullでは無いので、「1」が返却されます。 s = null; length = s.sure().length() // nullなのでNullPointerExceptionが送出されます。
実際に送出された例外のStackTraceです。
Exception in thread "main" jet.runtime.Intrinsics$JetNullPointerException at nullCheck.namespace.main(hoge.kt:21) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
例4:Elvis operator(null値の判定で使用する)
nullの場合に、何か別の値を返す場合に使用するらしいです。例えば、Javaで文字列の長さを返す際にその値がnullの場合は、-1を返すとする場合は、
以下のように三項演算子を使うと思います。
s != null ? s.length() : -1これを、KotlinのElvis operatorで書くとこんな感じになります。
null意外の場合は、「:」の左側の式の結果(この場合だとlengthの結果)が返却され、nullの場合は「:」の右側の結果が返却されるようです。
b?.length() ?: -1
例5:安全なキャスト
安全にキャストを行うためには、「as?」を使用する必要があります。「as?」を使用した場合、その型にキャストができない方の場合にはnullが返却されます。
「as」を使用した場合には、TypeCastExceptionが送出されます。
val s = "123"; var i1:Int = s as Int // これは、TypeCastExceptionが送出されます。 var i2:Int? = s as? Int // これは、nullが返却されます。
Collection系のオブジェクトから値を代入したときにその値がnullだった場合どんな動きをするんだ?
これは、コンパイル時にチェックするのは不可能だし、どんな動きをするんだろう?と思って調べてみました。
調べたときにつかったコード
val arr:Array<String?> = array(null) // nullを許容するArray val s:String = arr[0] // nullが代入出来る。 s.length() // ぬるぽではなく、TypeCastExceptionが発生
nullが代入されたnull不可変数を参照したら、まさかのTypeCastExceptionが発生しました。
これだったらNullPointerExceptionのが分かりやすいんじゃね?TypeCastExceptionじゃ原因特定出来る気がしない・・・。
この辺はこれから改善されていくかもしれませんが。
ScalaのOptionなるものがあったからこんどScalaのほうもちゃんと勉強してみよう。