Osmanthus

空想具現化


  • 首页
  • 归档
  • 分类
  • 标签
  • 关于
  •   

© 2024 Homurax

UV: | PV:

Theme Typography by Makito

Proudly published with Hexo

Kotlin 基础11 - 使用 DSL 构建专用的语法结构

发布于 2020-04-27 Kotlin  Kotlin 基础 

使用 infix 函数构建更可读的语法

Kotlin 提供了一种高级语法糖特性:infix 函数。

infix 函数把编程语言调用的语法规则调整了一下,比如 A to B 这样的语法结构,实际上等价于 A.to(B) 的写法。

infix fun String.beginsWith(prefix: String) = startsWith(prefix)
infix fun <T> Collection<T>.has(element: T) = contains(element)
infix fun <A, B> A.with(that: B): Pair<A, B> = Pair(this, that)

fun test() {
    if ("Hello Kotlin" beginsWith "Hello") {
        // TODO
    }
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
    if (list has "") {
        // TODO
    }

    val map = mapOf("Apple" with 1, "Banana" with 2, "Orange" with 3)
}

mapOf() 函数实际上接收的是一个 Pair 类型的可变参数列表。而 to() 函数就是创建并返回了一个 Pair 对象。

/**
 * Creates a tuple of type [Pair] from this and [that].
 *
 * This can be useful for creating [Map] literals with less noise, for example:
 * @sample samples.collections.Maps.Instantiation.mapFromPairs
 */
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

使用定义泛型函数的方式将 to() 函数定义到了 A 类型下。并且接收一个 B 类型的参数,因此 A 和 B 可以是两种不同类型的泛型。

使用 DSL 构建专用的语法结构

领域特定语言是编程语言赋予开发者的一种特殊能力,通过它可以编写出一些看似脱离其原始语法结构的代码,从而构建出一种专有的语法结构。使用 infix 函数构建出的特有语法结构就属于 DSL 。

class Dependency {
    val libraries = ArrayList<String>()
    fun implementation(lib: String) {
        libraries.add(lib)
    }
}

fun dependencies(block: Dependency.() -> Unit): List<String> {
    val dependency = Dependency()
    dependency.block()
    return dependency.libraries
}

fun main() {
    val libraries = dependencies {
        implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
        implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.2.0")
    }
    for (lib in libraries) {
        println(lib)
    }
}

语法结构使用上和 build.gradle 文件中使用的语法结果并不完全相同,这主要是因为 Kotlin 和 Groovy 在语法层面还是有一定差别的。
这种语法结构比直接调用 Dependency 对象的 implementation() 方法要更加直观一些,需要添加的依赖库越多,使用 DSL 写法的优势就会越明显。


class Td {
    var content = ""
    fun html() = "\n\t\t<td>$content</td>"
}
class Tr {
    private val children = ArrayList<Td>()
    fun td(block: Td.() -> String) {
        val td = Td()
        td.content = td.block()
        children.add(td)
    }
    fun html(): String {
        val builder = StringBuilder()
        builder.append("\n\t<tr>")
        for (childTag in children) {
            builder.append(childTag.html())
        }
        builder.append("\n\t</tr>")
        return builder.toString()
    }
}
class Table {
    private val children = ArrayList<Tr>()
    fun tr(block: Tr.() -> Unit) {
        val tr = Tr()
        tr.block()
        children.add(tr)
    }
    fun html(): String {
        val builder = StringBuilder()
        builder.append("<table>")
        for (childTag in children) {
            builder.append(childTag.html())
        }
        builder.append("\n</table>")
        return  builder.toString()
    }
}
fun table(block: Table.() -> Unit): String {
    val table = Table()
    table.block()
    return table.html()
}
fun main() {

    val html = table {
        tr {
            td { "Apple" }
            td { "Grape" }
            td { "Orange" }
        }
        tr {
            td { "Pear" }
            td { "Banana" }
            td { "Watermelon" }
        }
    }
    println(html)
}

DSL 中也可以使用 Kotlin 的其他语法特性。

val html = table {
    repeat(2) {
        tr {
            val fruits = listOf("Apple", "Grape", "Orange")
            for (fruit in fruits) {
                td { fruit }
            }
        }
    }
}

 上一篇: Kotlin 基础12 - Kotlin 与 Java 互操作 下一篇: Kotlin 基础10 - 使用协程 

© 2024 Homurax

UV: | PV:

Theme Typography by Makito

Proudly published with Hexo