読者です 読者をやめる 読者になる 読者になる

算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

ことりんのNULL安全

Kotlin

※下記URLのサイトを参考にしました。英語、または技術的な知識が至らず、内容に誤りが含まれるおそれがありますので、ご了承ください。
※本エントリは参考サイトの翻訳をベースに、加筆・変更を施した構成となっています。

参考サイト http://confluence.jetbrains.net/display/Kotlin/Null-safety

null許容型と非null型

Javaプログラミングにおいて最も一般的な落とし穴のひとつは、NullPointerExceptionとなるようなnull参照のメンバにアクセスすることです。なぜなら、Javaプログラムでは実質的に、どの参照もnullを持つことができるからです。私たちがカジュアルな略称(NPE)で呼ぶそれは、非常によく起こります。

Kotlinの型システムには、私たちのコードからNPEを排除する狙いがあります。NPEの考えられる原因は次の3つだけです。

  1. 明示的にNPEを投げる
  2. NPEを起こす外部のJavaコードを呼び出す
  3. 初期化(コンストラクタで利用可能な初期化されていないthisがどこかで使用されている*1)に関してデータの矛盾がある

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 -1

http://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?.name

http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用

このような呼び出しチェーンは、いずれかのプロパティがnullならnullを返します。

エルビス演算子

null許容型のrがあるとき「rがnullでないならばそれを使い、そうでないなら非null値のxを使用する」と言うことができます。

val l : Int = if (b != null) b.length() else -1

http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用

エルビス演算子(?:)で表現することもできます。

val l = b?.length() ?: -1

http://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
    this

http://confluence.jetbrains.net/display/Kotlin/Null-safety より引用

述語演算子

Kotlinでは、 a?b() と書くことができます(b()は aのメソッド)。これを述語演算子(predicate operator)と呼び、次のように動作します。

if (a != null && a.b()) a else null

http://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? Int

http://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による)

*1:訳に自信なし(-_-;

*2:邦訳:パラメータの正当性を検査する

*3:邦訳:nullではなく、空配列か空コレクションを返す