この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
毎日Kotlinシリーズです。
このシリーズを初めての方はこちらです。「毎日Kotlin」はじめました | Developers.IO
問題
Builders how it works | Try Kotlin
Look at the questions below and give your answers
- In the Kotlin code
tr {
td {
text("Product")
}
td {
text("Popularity")
}
}
'td' is:
a. special built-in syntactic construct
b. function declaration
c. function invocation
- In the Kotlin code
tr (color = "yellow") {
td {
text("Product")
}
td {
text("Popularity")
}
}
'color' is:
a. new variable declaration
b. argument name
c. argument value
- The block
{
text("Product")
}
from the previous question is:
a. block inside built-in syntax construction td
b. function literal (or "lambda")
c. something mysterious
- For the code
tr (color = "yellow") {
this.td {
text("Product")
}
td {
text("Popularity")
}
}
which of the following is true:
a. this code doesn't compile
b. this refers to an instance of an outer class
c. this refers to a receiver parameter TR of the function literal:
tr (color = "yellow") {
this@tr.td {
text("Product")
}
}
import Answer.*
enum class Answer { a, b, c }
val answers = mapOf<Int, Answer?>(
1 to null, 2 to null, 3 to null, 4 to null
)
狙い
ここで考えて欲しい問題の意図はなんだろうか?
Kotlinの豊かなDSL。使いこなすと強力な武器。いままで解いてきた問題の集大成!
解答例
1 to c, 2 to b, 3 to b, 4 to c
mapOfをtoを記述できるようにしたものです。
toはただの拡張関数でPairを作ります。
public infix fun <A, B> A.to(that: B): kotlin.Pair<A, B>
本来なら
1.to(Answer.a)
なんですが、infixがついているので、スペース区切りで呼べます。
1 to c
種はこれだけなんですが、簡潔な記述に見えますね!
HtmlBuilderの補足
[毎日Kotlin] Day40. Html builderの解説が本日の問題説明文であります。
Html builders | Try Kotlinの定義はこちらを参照。
return html {
table {
tr {
td {
text("Product")
}
td {
text("Price")
}
td {
text("Popularity")
}
}
}
}.toString()
頭から見ていきましょう。
fun html(init: Html.() -> Unit): Html = Html().apply(init)
Html()をnewして、一時的な拡張関数initを引数でもらうことで、Htmlの初期化処理を外からできるようにしています。html {}の内部のthisはHtmlオブジェクトのことです。
tableの定義を見てみよう。
fun Html.table(init : Table.() -> Unit) = doInit(Table(), init)
class Table: Tag("table")
fun <T: Tag> Tag.doInit(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
open class Tag(val name: String) {
val children = mutableListOf<Tag>()
val attributes = mutableListOf<Attribute>()
override fun toString(): String {
return "<$name" +
(if (attributes.isEmpty()) "" else attributes.joinToString(separator = " ", prefix = " ")) + ">" +
(if (children.isEmpty()) "" else children.joinToString(separator = "")) +
"</$name>"
}
}
Htmlの拡張関数としてtableが定義されています。
Htmlと同じようにTable初期化処理を一時的な拡張関数initでもらっています。 それをHtmlの子要素に入れています。
tr,tdも同じように、自身を外部から初期化処理をもらい、親のchildernに自身を追加する流れになっています。
最後のtoString()のときに自分と子要素を結合していっています。
open class Tag(val name: String) {
val children = mutableListOf<Tag>()
val attributes = mutableListOf<Attribute>()
override fun toString(): String {
return "<$name" +
(if (attributes.isEmpty()) "" else attributes.joinToString(separator = " ", prefix = " ")) + ">" +
(if (children.isEmpty()) "" else children.joinToString(separator = "")) +
"</$name>"
}
}
やってることはそんなに難しいことをしていませんが、ラムダと拡張関数のオンパレードでややこしくみえます。その分、使用者は簡潔に、適切に記述できるようになります。
DSLでよく使えれる機能もチェック
いつも参考にさせていただいております。ありがとうございます!
あとがき
Day42.でまたお会いしましょう。 そして最終回。