この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
毎日Kotlinシリーズです。
このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO
問題
The function apply | Try Kotlin
The previous examples can be rewritten using the library function apply (see examples below). Write your own implementation of this function named 'myApply'
fun <T> T.myApply(f: T.() -> Unit): T { TODO() }
fun createString(): String {
return StringBuilder().myApply {
append("Numbers: ")
for (i in 1..10) {
append(i)
}
}.toString()
}
fun createMap(): Map<Int, String> {
return hashMapOf<Int, String>().myApply {
put(0, "0")
for (i in 1..10) {
put(i, "$i")
}
}
}
狙い
ここで考えて欲しい問題の意図はなんだろうか?
スコープ関数を自作して仕組みを理解しよう。プチBuilderとして大活躍すること間違い無し。
解答例
fun <T> T.myApply(f: T.() -> Unit): T {
f()
return this
}
これだけなんです。こういうちょっとしたコードが意外と便利だったりするんです。これでも十分なんですが、これだと関数呼び出し分遅くなってしまいます。
// 拡張関数を使わない、ベタな実装
fun createString(): String {
val builder = StringBuilder()
builder.append("Numbers: ")
for (i in 1..10) {
builder.append(i)
}
return builder.toString()
}
// 拡張関数つかった実装
// 関数呼び出し分遅い
fun createString(): String {
return StringBuilder().myApply {
append("Numbers: ")
for (i in 1..10) {
append(i)
}
}.toString()
}
文法が簡単になるかわりに速度を犠牲に。。。っと諦めようとしてるあなた!Kotlinなら**inline**という強い見方があります。単純にinlineを付けてあげます。
inline fun <T> T.myApply(f: T.() -> Unit): T {
f()
return this
}
こうすると、ラムダ式の部分がコンパイル時に展開されて、コンパイル後は、以下と同じようなコードになっています。
fun createString(): String {
val builder = StringBuilder()
builder.append("Numbers: ")
for (i in 1..10) {
builder.append(i)
}
return builder.toString()
}
おまけでJavaに戻した時のコード。
@NotNull
public static final String createString() {
Object $receiver$iv = new StringBuilder();
StringBuilder $receiver = $receiver$iv;
$receiver$iv.append("Numbers: ");
int i = 1;
for(byte var3 = 10; i <= var3; ++i) {
$receiver.append(i);
}
String var10000 = $receiver$iv.toString();
Intrinsics.checkExpressionValueIsNotNull(var10000, "StringBuilder().myApply … }\n }.toString()");
return var10000;
}
あとがき
Day40.でまたお会いしましょう。