Kotlin之使用DSL构建专有的语法结构

news2024/11/16 11:39:50

DSL的全称是领域特定语言(Domain Specific Language),它是编程语言赋予开发者的一种特殊能力,通过它我们可以编写出一些看似脱离其原始语法结构的代码,从而构建出一种专有的特殊结构。
Kotlin也是支持DSL的,并且在Kotlin中实现DSL的方式并不固定,比如infix函数构建出的特有语法结构就属于DSL。不过Kotlin中最常见的实现DSL方式是通过高阶函数。
其实DSL我们一直都在用,比如我们想要在项目中添加一些依赖库,需要在build.gradle文件中编写如下内容:

dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
}

Gradle是一种基于Groovy语言的构建工具,因此上述的语法结构其实就是Groovy提供的DSL功能。
借助Kotlin的DSL,我们也可以实现类似的语法结构。
首先新建一个DSL.kt文件,然后在里面实现定义一个Dependency类,代码如下所示

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

这里我们使用了一个List集合来保存所有的依赖库,然后又提供了一个implementation()方法,用于向List集合中添加依赖库
接下来定义一个dependencies高阶函数,代码如下所示

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

可以看到,dependencies函数接收一个函数类型参数,并且该参数是定义到Dependency类当中的,因此调用它的时候需要先创建一个Dependency的实例,然后再通过该实例调用函数类型参数,这样传入的Lambda表达式就能得到执行了。最后我们将Dependency类中保存的依赖库集合返回。
我们可以通过dependencies函数的返回值来获取所有添加的依赖库,代码如下所示:

fun main() {
    val libraries = dependencies {
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2")
        implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
    }
    for(lib in libraries){
        println(lib)
    }
}

这里用一个libraries变量接收dependencies函数的返回值,然后使用for -in循环将集合中的依赖库全部打印出来。
在这里插入图片描述
可以看到,已经成功将使用DSL语法结构添加的依赖库全部获取到了。
在前端开发中,网页的展示都是由浏览器解析HTML代码来实现的。HTML中定义了很多标签,其中< table>标签用于创建一个表格,< tr >标签用于创建表格的行,< td >标签用于创建单元格。将这3种标签嵌套使用,就可以定制出包含任意行列的表格了。
例如:

<table>
<tr>
<td>Apple</td>
<td>Grape</td>
<td>orange</td>
</tr>
<tr>
<td>Pear</td>
<td>Banana</td>
<td>Watermelon</td>
</tr>
</table>

这样就会创建一个两行三列的表格,修改后缀名,将文件名改为test.html然后双击文件,使用浏览器打开,显示如下:
在这里插入图片描述
如果有一个需求,要求我们在Kotlin中动态生成表格所对应的HTML代码,最直接的方式是字符串拼接,但是这种方式比较繁琐,而且代码难以阅读。
而借助DSL,我们可以使用一种方便的语法结构来动态生成表格所对应的HTML代码
定义一个Td类,代码如下

class Td{
    var content=""
    fun html()="\n\t\t<td>$content</td>"
}

由于< td >标签表示一个单元格,其中必然是要包含内容的,因此这里我们使用了一个content字段来存储单元格中显示的内容。另外,还提供了一个html()方法,当调用这个方法时就返回一段< td >标签的HTML代码,并将content中存储的内容拼接进去。为了让最终输出的结果更加直观,使用了\n和\t转义符来进行换行和缩进。
接下来定义一个Tr类,代码如下所示:

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()
    }
}

由于< tr >标签表示表格的行,它是可以包含多个< td >标签的,因此我们创建了一个children集合,用于存储当前Tr所包含的Td对象。接下来提供了一个td()函数,它接收一个定义到Td类中并且返回值是String的函数类型参数。 当调用td()函数时,会先创建一个Td对象,接着调用函数类型参数并获取它的返回值,然后赋值到Td类的content字段当中。这样就可以将调用td()函数时传入的Lambda表达式的返回值赋给content字段了。最后将创建了的td对象添加到children集合当中。
另外,Tr类中也定义了一个html()方法,它的作用和刚才Td类当中的html()方法一致。只是由于每个Tr都可能会包含很多个Td,因此我们需要使用循环来遍历children集合,将所有的子Td都拼接到< tr >标签中,从而返回一段嵌套的HTML代码。
定义好了Tr类之后,我们就可以使用如下语法格式来构建表格中的一行数据:

val tr=Tr()
tr.td{"Apple"}
tr.td{"Orange"}
tr.td{"Grape"}

接下来再定义一个Table类,代码如下

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()
    }
}

现在我们就可以使用如下语法结构来创建一个表格了:

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

还可以再进一步简化,定义一个table()函数,代码如下

fun table(block:Table.()->Unit):String{
    val table=Table()
    table.block()
    return table.html()
}

最后我们就可以使用如下语法结构动态生成一个表格所对应的HTML代码了:

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

在这里插入图片描述
另外,在DSL中也可以使用Kotlin的其他语法特性,比如通过循环来批量生成< tr >和< td >标签:

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/184731.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CF——1766C - Hamiltonian Wall

题目链接 1766C - Hamiltonian Wall Rating&#xff1a;1300 题目描述 Sir Monocarp Hamilton is planning to paint his wall. The wall can be represented as a grid, consisting of 2 rows and m columns. Initially, the wall is completely white. Monocarp wants to p…

Leetcode力扣秋招刷题路-0101

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 101. 对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 示例 2&#xff1…

[LeetCode周赛复盘] 第 330 场周赛20230129

[LeetCode周赛复盘] 第 330 场周赛20230129 一、本周周赛总结二、 [Easy] 6337. 统计桌面上的不同数字1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6338. 猴子碰撞的方法数1. 题目描述2. 思路分析3. 代码实现四、[Hard] 6339. 将珠子放入背包中1. 题目描述2. 思路分析3. 代…

过年了,给网站加个灯笼+飘雪效果!

过年了&#xff0c;下面分享一个网站的特效&#xff0c;给网站添加一个新春灯笼和飘雪的效果&#xff0c;过年期间多一点年味。灯笼特效下面是css样式&#xff0c;可以放在公共样式中&#xff1a;.deng-box{position:fixed;top:-40px;right:150px;z-index:9999;pointer-events:…

【音视频工具】前端屏幕录制工具 + 录制<video>标签内容

一、录制的实现思路 1.开始录制、停止录制、下载视频 2.Blob介绍 3.概念 var mediaRecord //用于录制视频 var mediaStream //视频流 var videoBuffer [] //保存的视频数据二、屏幕录制工具 下载地址&#xff1a; https://chrome.google.com/webstore/detail/tampermonkey…

k8s之POD资源限制和健康监测

写在前面 本文一起看下POD的资源限制配置和健康监测的相关内容。 1&#xff1a;资源限制 如果是不对POD设置资源限制的话&#xff0c;若任由其占用系统资源&#xff0c;可能会造成非常严重的后果&#xff0c;所以我们需要根据具体情况来设置资源限制&#xff0c;如使用多少内…

怎样把截图转换成文字?三分钟教会你如何截图转文字

在日常的学习中&#xff0c;当你在网上看到一篇文章&#xff0c;而当中的某一段话很适合拿来用在自己的写作上&#xff0c;但是你却无法直接将其复制粘贴下来&#xff0c;只能先截图下来再手动输入&#xff0c;这种方法虽然可行&#xff0c;但比较消耗时间和精力&#xff0c;那…

详细图解LeetCode经典链表算法题

文章目录链表类型算法题一、链表介绍本文使用的Java中链表类&#xff1a;二、链表基础题1、数组转链表代码&#xff1a;测试&#xff1a;2、单链表翻转题目&#xff1a;代码&#xff1a;解析&#xff1a;测试&#xff1a;补充&#xff1a;3、合并两个有序链表题目&#xff1a;解…

顺应信创发展,君子签电子签章方案全面适配信创环境

信创产业作为战略性新兴产业&#xff0c;近年来&#xff0c;国家不断出台相关政策对行业的发展进行支持。 2018年我国首次将信创纳入国家战略&#xff0c;并提出了 “28N”应用体系&#xff0c;信创发展步入“快车道”&#xff1b;2020年起&#xff0c;信创产业由党政逐渐向其…

垃圾收集器必问系列—ZGC

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 人的一切痛苦&#xff0c;本质上都是对自己的无能的愤怒。——王小波 文章目录Region布局读屏障染色指针染色指针的优势运作过程ZGC的优缺点ZGC有人称它为Zero GC&#xff0c;其实“Z”并非什么专业名词的缩写…

vue前端框架课程笔记(二)

目录计算属性问题引入插值语法实现methods配置属性实现computed配置属性一些问题getter与setter注意监视属性问题引入computed和methods实现watch属性watch属性简介computed与watch的比较注意本博客参考尚硅谷官方课程&#xff0c;详细请参考 【尚硅谷bilibili官方】 本博客以…

千峰网络安全笔记(前三讲)

典中典 《c语言从研发到脱发》 《C从入门到放弃》 《Java从跨平台到跨行业》 《Ios开发从入门到下架》 《Android开发大全——从开始到转行》 《PHP由初学至搬砖》 《黑客攻防:从入门到入狱》 《Mysql从删库到跑路》 《服务器运维管理从网络异常到硬盘全红》 《服务器运维管理…

CentOS 8 中dnf管理器如何仅下载不安装软件

在某些情况下&#xff0c;我们希望从命令行下载特定或一组 RPM 包而不安装它。虽然我们可以使用 wget 命令下载&#xff0c;但 wget 不会下载安装包的依赖项。在 CentOS 8 中DNF&#xff08;或 yum&#xff09;是一个命令行包管理工具。使用 DNF我们可以安装、更新和删除 rpm 包…

HTTP协议学习

一、http请求协议1、常见请求头accept:浏览器通过这个头告诉服务器&#xff0c;它所支持的数据类型Accept-Charset: 浏览器通过这个头告诉服务器&#xff0c;它支持哪种字符集Accept-Encoding&#xff1a;浏览器通过这个头告诉服务器&#xff0c;支持的压缩格式Accept-Language…

114.简单的动态切换app的图标

1.第一步 通过activity-alias别名实现&#xff0c;manifest 这里写的是一个默认的图标Default和一个需要切换的图标Test&#xff0c;以及一个默认的首页面HomeActivity&#xff1a; <!-- 默认的图标--> <activity-aliasandroid:name".activity.Default"and…

C语言入门教程||C语言 运算符||C语言 判断

C语言 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C 语言内置了丰富的运算符&#xff0c;并提供了以下类型的运算符&#xff1a; 算术运算符关系运算符逻辑运算符位运算符赋值运算符杂项运算符 本章将逐一介绍算术运算符、关系运算符、逻辑运算符、位运算…

基于android的大学生图书管理系统app

需求信息&#xff1a; 1.学生用户端 查询图书。学生用户可以对馆内图书资料进行简单和高级的查询。 预约图书。当查询时发现要借阅的图书已被借阅&#xff0c;可以提前预约。 挂失图书。图书不慎丢失&#xff0c;可以在学生端实现挂失。 2.管理员端 学生用户管理。管理员可以对…

LeetCode——2309. 兼具大小写的最好英文字母

一、题目 给你一个由英文字母组成的字符串 s &#xff0c;请你找出并返回 s 中的最好英文字母。返回的字母必须为大写形式。如果不存在满足条件的字母&#xff0c;则返回一个空字符串。 最好 英文字母的大写和小写形式必须 都 在 s 中出现。 英文字母 b 比另一个英文字母 a …

Python 操作pdf(pdfplumber读取PDF写入Exce)

1. Python 操作pdf(pdfplumber读取PDF写入Exce) 1.1 安装pdfplumber模块库: 安装pdfplumber: pip install pdfplumber 复制代码 pdfplumber.PDF类 pdfplumber.PDF类表示单个PDF ,并具有两个主要属性: 属性说明pdf.metadata从PDF的Info中获取元数据键/值对字典。通常包括&q…

【HBase——陌陌海量存储案例】4. Apache Phoenix 介绍与安装

5. 性能问题 Hbase默认只支持对行键的索引&#xff0c;那么如果要针对其它的列来进行查询&#xff0c;就只能全表扫描之前介绍的查询是使用scan filter组合来进行查询的&#xff0c;但查询地效率不高&#xff0c;因为要进行顺序全表扫描而没有其他索引。如果数据量较大&#…