【Kotlin練習問題】Null許容型(Nullable)
Null許容型とは
Kotlinの型システムでは、null参照の危険性を排除することに重点が置かれています。
Javaを含む多くのプログラミング言語における落とし穴の一つが、null参照のメンバにアクセスしようとすることによりnull参照エクセプション、いわゆるヌルポ(NullPointerException)を起こしてしまうことです。
Kotlinの型システムはnullである可能性のある参照(null許容参照)とnullがあり得ない参照(非null許容参照)とを区別します。例えば、通常のString型の変数はnullを持つことはあり得ません。
var a: String = "abc" // 通常の初期化ではデフォルトでnullはあり得ない a = null // コンパイルエラーが起こります
nullを許容するには、String?と書いて、null許容Stringとして宣言する必要があります。
var b: String? = "abc" // nullがありえる
b = null // OK
print(b)
a上(nullがあり得ない)でメソッドを呼び出したり、aのプロパティにアクセスしようとしても、ヌルポが発生しないが保証されているので、以下のようなコードを安全に行うことができます。
val l = a.length
ですが、もし同じようにbのプロパティにアクセスしようとすると、nullセーフではないため、コンパイルエラーが起こります。
val l = b.length // エラー: 変数'b' はnullの可能性がある
しかしbのプロパティにアクセスする必要がある場合もありますよね?そのためにはいくつか方法があります。
条件文でnullチェックをする
第一の方法は、明示的にbがnullであるかをチェックすることで、nullの場合かそうでない場合かで別々の処理を行わせることができます。
val l = if (b != null) b.length else -1
コンパイラーはあなたが行ったnullチェックに関する情報を辿るので、if文の中ではlengthを呼び出すことができるようになるのです。以下のように、より複雑な条件文もありえます。
val b: String? = "Kotlin" if (b != null && b.length > 0) { print("String of length ${b.length}") } else { print("Empty string") }
これはnullチェックのあとでbの状態が変わり得ない状況でのみ機能するということを覚えておいてください。つまりbがnullチェックとbを使用する間に、bの内容が書き換えられないローカル変数であるということを意味します。
そうでないと、nullチェックのあとにbがnullに変わってしまう可能性があり、チェックの意味がなくなってしまうからです。
安全な呼び出し
二つ目の方法は安全呼び出し演算子である ?. を使うことです。
val a = "Kotlin"
val b: String? = null
println(b?.length)
println(a?.length) //不必要な安全呼び出し
これはbがnullでなければb.lengthを返し、そうでなければnullを返します。これで返ってくる式の型はInt?です。
安全な呼び出しはチェーンの中で特に便利です。例えば、ボブ(Bob)という従業員がいて(もしかしたらいないかもしれない)、ある部署(Department)に配属され(されないかもしれない)、そこには部長(Head)がいます(いないかもしれない)。そういった場合での部長の名前を取得するコードは以下のようになります。
bob?.department?.head?.name
上のチェーンではbob、department、headのうち、どれか一つでもnullだった場合はnullを返します。
nullでない値に対してのみ、特定の処理を行わせたい場合は、letと安全呼び出し演算子を併用することで実現できます。
val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
item?.let { println(it) }
}
上記のコードでは、item?の中身がnullでない場合はそれを出力します。nullの場合は無視されます。ですので、実行結果は”Kotlin”のみが出力されます。
また、安全な呼び出しは代入の左辺に来ることもあります。その場合、左辺(代入される側)にあるチェーン内の一つでもnullであった場合、その代入はスキップされ、右辺の式はそれ以上評価されません。
// もしpersonかperson.departmentがnullの場合、関数getManager()は呼び出されない。
person?.department?.head = managersPool.getManager()
問題
以下のJavaコードを一つのif文だけを使うようにKotlinで書き換えてください。(下のKotlinコードのTODO()を書き換えてください)
Javaコード:
public void sendMessageToClient(
@Nullable Client client,
@Nullable String message,
@NotNull Mailer mailer
) {
if (client == null || message == null) return;
PersonalInfo personalInfo = client.getPersonalInfo();
if (personalInfo == null) return;
String email = personalInfo.getEmail();
if (email == null) return;
mailer.sendMessage(email, message);
}
Kotlinコード:
fun sendMessageToClient(
client: Client?, message: String?, mailer: Mailer
) {
TODO()
}
class Client(val personalInfo: PersonalInfo?)
class PersonalInfo(val email: String?)
interface Mailer {
fun sendMessage(email: String, message: String)
}
Kotlin Playgroundでコードの編集・動作確認ができます。
[expander_maker id=”1″ more=”答え” less=”非表示”]答え
fun sendMessageToClient(
client: Client?, message: String?, mailer: Mailer
) {
val email = client?.personalInfo?.email
if (email != null && message != null) {
mailer.sendMessage(email, message)
}
}
class Client(val personalInfo: PersonalInfo?)
class PersonalInfo(val email: String?)
interface Mailer {
fun sendMessage(email: String, message: String)
}
[解説]
null許容型を使用しているので、Javaのように最初にclientとmessageのnullチェックをする必要はありません。val email = client?.personalInfo?.emailで、clientとpersonalInfoの両方がnullでなければ、emailを代入しています。
その後で、emailとmessageがnullでなければ、sendMessageメソッドに値を渡しています
[/expander_maker]その他の問題はこちらからどうぞ。
完全無料で通えるプログラミングスクール
プログラミング学習はどうしても一人だとつまづいてしまう時がきます。調べればわかることも少なくないですが、最初のうちは調べ方もわからないことが多いため、あまり効率的ではありません。
効率的かつ挫折せずにプログラミングを学習したい方はスクールを検討してみるのも一つの手です。
中には無料で通えるスクールや、就職保証をしてくれるスクールなどもあるので、きっとあなたの目的に応じて最適のスクールが見つかります!以下の記事で評判がよく特におすすめのスクールをいくつかピックアップしているので、スクール選びで後悔したくない方は御覧ください!
おすすめ書籍
Kotlinの文法をまず学びたい!という方には以下の書籍がおすすめです。Kotlinは日本語書籍がまだ豊富とは言えない状況ですが、細かく解説されており、Kotlin入門者のかたでもつまずくことなく学習できると思います。
[itemlink post_id=”1743″]実際にアプリを作りながら覚えていきたい!という方には以下もお勧めです。はじめに上の書籍で文法をさらっと学んでから取り組むのがお勧めです。
[itemlink post_id=”1745″]