この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
毎日Kotlinシリーズです。
このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO
問題
JavaからKotlinに変換してみよう。
Read about null safety and safe calls in Kotlin and rewrite the following Java code using only one if expression:
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);
}
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では、Nullを扱う機能がたくさんあります。Null安全ではなく、Nullを扱いやすくするテクニックだと思います。 解答はたくさんできるので、Java -> Kotlinにしてみましょう。
messageがnullの時送らないなら、nullableにするなっていうそもそも論は、一旦おいておきましょう。
解答例
fun sendMessageToClient(
client: Client?, message: String?, mailer: Mailer
) {
// clientもpersonalInfoもnullじゃない時にemailを返す。それ以外はnullになる。
val email = client?.personalInfo?.email
if (email != null && message != null) {
mailer.sendMessage(email, message)
}
}
nullableなメンバー変数にアクセスする時は、?つなぎでアクセスできます。
?つなぎの場合は、実際nullでない時に、右側が実行されnullの時はそれ以上何もしない動作になります。
エルビス演算子
?:(エルビス演算子)を使うともっとスッキリするかもしれません。Kotlinには三項演算子はないのですが、nullの時に何かする場合は?:が使えます。
fun sendMessageToClient(
client: Client?, message: String?, mailer: Mailer
) {
//messageがnullの時、早期return
message ?: return
val email = client?.personalInfo?.email ?: return
mailer.sendMessage(email, message)
}
もしmessage、emailがnullの時はデフォルトで空文字や定型文をいれたいとします。
val msg = message ?: "こんにちは"
val email = client?.personalInfo?.email ?: "hoge@hoge.com"
結構使う局面は多いので覚えておきたいです。
スコープ関数を使う
fun sendMessageToClient(
client: Client?, message: String?, mailer: Mailer
) {
message ?: return
client?.personalInfo?.email?.let {
mailer.sendMessage(it, message)
}
}
emailをいちいち一時的な変数にいれるのが面倒ですよね。その場合はスコープ関数が活躍するかもしれません。今回はletを取り上げますが、今後の課題ででてきたときに他のも解説します。
let kotlin/Standard.kt at 1.2.0 · JetBrains/kotlin
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
やっていることはblock(this)なので一時変数をmailer.sendMessage(email, message)と同じなんですが、emailを?つなぎで?.let{}としていることで、emailをnot nullにしている。
つまり、以下とだいだい同じことをしている。
val email = client?.personalInfo?.email
if (email != null) {
mailer.sendMessage(email, message)
}
スコープ関数を使いこなすと便利なので使っていこう。たくさんあって同じことができるので使い分けに少しコツありますが、便利には違いない!覚えよう。
あとがき
Day9.でまたお会いしましょう。