前回はKotlinにおけるクラスとその周辺の機能、
背景
java.
は、null
を使用してしまうことでスローされる例外です。具体的にはString
型の変数にnull
を代入しておき、length
メソッドを呼び出した場合にNullPointerException
、
null
は、findUserById
のようなメソッドがUser
クラスのインスタンスを返す代わりにnull
を返すと言った具合です。
このような観点で、null
は便利に働きます。しかしこのnull
のおかげで筆者たちは見たくもない例外、null
チェック、if
文などでnull
でないことを確認すれば回避できるのですが、
null
を返さないことがわかっているメソッドの戻り値に対してnull
チェックはしないのが普通だと思います。ここが重要なのですが、null
を返し得ないメソッドがあっても、null
になり得ます。つまりnull
チェックをすべきものと、null
チェックが不要なものがごちゃまぜになっているので、
null
と上手に付き合う方法
null
かもしれないものと、null
ではないものを区別するための方法が世の中にはいくつかあります。
メソッドシグネチャの工夫
原始的な方法です。null
を返す可能性があるメソッドのシグネチャを工夫して、getNameOrNull
のような名前のメソッドです。名前を見ればnull
が返されるかもしれないことに気づくわけです。
静的解析ツール
メソッドにアノテーションを付けて、getName
メソッドがnull
を返すかもしれない場合には@Nullable String getName() {...}
と記述し、null
を返し得ない場合には@Nonnull StringgetName() {...}
と記述します。
型で表現
存在しない可能性のある値を表現するためにnull
の代わりに新しく定義した型を使う方法です。具体的にはJava SE 8で導入されたjava.
クラスです。値が存在しないときにはOptional#empty
で返されるオブジェクトを使用し、Optional#of
の引数に渡してラップします。Optional
型とそれ以外の型で、Optional
にはさまざまな便利なメソッドが提供されています。
Kotlinのnull安全
静的解析ツールやOptional
を使うことはとても良いことです。しかし繰り返しになりますが、null
になり得ます。すべてを台無しにするコードをご覧ください
そこでKotlinのnull
安全機構の登場です。Kotlinではnull
の可能性のある値null
ではない値
Optional
を使う方法とは異なり、
基本的な使い方
前回、a
はString
型です。ここでは型を明示していますが省略しても問題ありません。var
キーワードにより変更可能な変数として宣言しているので"Goodbye"
を代入できます。しかしその次の行のnull
を代入する部分でコンパイルエラーが起こります。変数a
はNotNullとして宣言されているのでnull
の代入をコンパイラが許しません! 逆を言えば、null
ではないと安心して使用できます。
では、null
を代入できるNullableな変数はどのように宣言すれば良いのでしょうか。簡単です。通常の型アノテーションのあとに?
を置くだけです。リスト3を見てください。変数bの型アノテーションがString?
になっています。これはnull
が代入可能なString
型」null
を代入していますが、"Hello"
はString?
ではなくString
として推論されるからです。
KotlinではNullableとNotNullを明確に区別することがわかりました。NotNullの変数にはnull
が入ってこないので、null
が入る可能性があるのでNPEが起こりそうです。ということでNPEを起こしてみましょう
String?
な変数s
にnull
を代入して初期化しています。このs
にlength
メソッドを呼び出してNPEを起こそうとしています。が、
しかし現実問題、null
チェックすることです!
リスト5のように変数s
がnull
でないことを確認すると、s
をNotNullとして扱えるようになります。s
に"Hello"
が代入されていればリスト5を実行すると
Nullableの便利な機能
KotlinのNullableを使ううえで必要な知識は、null
チェックをするコードを書くのは退屈で面倒な作業ですので、
安全呼び出し
リスト5ではNullableのメソッドを呼び出すためにnull
チェックを行いました。これを簡潔に記述できるように、
リスト6の最初の行ではString?
型の変数s
のlength
メソッドを安全に呼び出しています。通常のメソッド呼び出しと異なるのは、?
を置くことです。これにより安全呼び出しとなり、
仮にs
がnull
だった場合には、null
を返します。1行目の安全呼び出し方式と、null
チェック方式は等価です。
安全呼び出しはメソッドチェーンを形成したい場合などではとくに効果を発揮しますfoo()?.bar()?.baz()
のように記述した場合、null
が返されても安全呼び出しがチェーンして最終的にnull
が返されるだけです。
デフォルト値
デフォルト値、null
だった場合に使用する値、?:
)s?.length()
は安全呼び出しにより、null
が返されます。null
が返された場合、0
を使用するように指定しています。null
でない場合、
禁断の!!演算子
最後に紹介するのは禁断の演算子です。
!!
演算子は、String?
である変数s
を!!
演算子により強制的にString
に変換しています。
この例はたまたまうまく行きました。しかしリスト10は実行時に例外を投げてクラッシュします。nullであるものに対して!!
演算子を使うとKotlinNullPointerException
を投げます。
null
の場合に例外が投げられる。これって結局今までと同じです。!!
演算子を使用したくなったらnull
チェックや安全呼び出し、!!
演算子を使いましょう。その際にはコメントとして使った理由や経緯を記しておくと良いでしょう。
1つ、!!
演算子を使いたくなるような例を示します。要素としてnull
を許容するリストList<T?>
)null
を排除してNotNullな要素だけの新しいリストList<T>
)
list.
により、null
以外のものに絞り込みます。しかしリストの型は依然List<T?>
のままです。そこで次のmap { it!! }
で強制的に要素の型をT?
からT
の変換しています。
実際には!!
演算子を使用せずに実装できますし、filterNotNull
はコレクションの標準メソッドとして提供されています。
標準拡張関数 let
Kotlinの標準ライブラリとして、
let
は、this
)f
)
関数リテラルに渡る唯一の引数it
)let
のレシーバと同一オブジェクトですので、it
は5
です。
さて、
リスト14で、succ
は関数であり、Int
のメソッドif
でnull
チェックしてNullableを安全にNotNullとして扱えるようにするしかありません。
ここでlet
の登場です。まず、let
は任意の型の拡張関数ですからa?.let {...}
のような安全呼び出しができます。そしてlet
の引数となる関数f
)null
のときはlet
拡張関数は呼び出せないので理に適っていますね。
ということでlet
を使うことでリスト14のsucc
適用の部分はリスト15のように書き換えられます。ちなみにリスト15はもっと簡潔に記述できます。第3回で紹介した定義済み関数の参照を得るスタイルで記述するとリスト16のようになります。
JavaですでにOptionalに親しんでいる人には、
Javaからコードを呼び出す
KotlinからJavaコードを呼び出す場合、hello
メソッドをKotlinから呼び出して結果を変数に代入する際、val msg= Sample.
のように型アノテーションを省略した場合、msg
はNotNullとしても、msg.
、msg?.length()
もコンパイラは許可します。もしmsg
がnull
であればmsg.
はNPEを起こします。
型アノテーションを明示することでNotNull/ ただし、 今回はKotlinのユニークな機能であるnull安全に関して、 少なくとも現時点のJavaではNotNullとNullableを厳格に区別することはできません。Kotlinではこの区別を厳格にすること、 いよいよ次回はKotlinによるAndroidプログラミングについて解説します。 2022年8月18日発売val msg: String = Sample.
とすれば、msg
はNotNullとして扱えます。もしString.
がnull
を返すようなことがあれば、msg
に代入する時点でjava.
を投げます。これは例外を投げるタイミングが早いという観点で、msg
の型アノテーションを省略したうえでNotNullとして扱うより幾分マシです。安全側に倒すならval msg: String?
とNullableを表明するとよいでしょう。
int
やboolean
などのプリミティブ型を返すメソッドが返す値は、まとめ
本誌最新号をチェック!
Software Design 2022年9月号
B5判/
定価1,342円
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
OSSソースコードリーディングのススメ
企業のシステムを支えるOSとエコシステムの全貌
今さら聞けないSSH
MySQLで学ぶ文字コード
新生