Kotlin 之 DSL 篇一

Kotlin DSL

什么是DSL

Domain Special Language

DSL是領域特定語言话侄,與通用語言不通基括,他只管他的領域,如:SQL河哑、正則表達式避诽;

特點:

  1. 一般DSL都是非常簡潔的;所以DSL璃谨,一般趨向于聲明式沙庐、這樣就需要解釋,也就是效率有些影響佳吞;

  2. 一般的編程語言基本是命令式的拱雏,定義了確定的步驟;

  3. 一般的DSL很難與通用編程語言的宿主應用結合起來底扳,
    gradle對應的Groovy是動態(tài)編程語言铸抑;

  4. DSL是有自己的結構的,DSL方法調用由DSL結構規(guī)定衷模;比如:SQL鹊汛;

Kotlin 中dsl 的特點:

Kotlin允許構建整潔API的功能包括:擴展函數(shù)、中綴調用阱冶、lambda柒昏、簡明語法和運算符重載;

  1. 完全靜態(tài)類型熙揍,優(yōu)勢:類型檢查,IDE提示等氏涩;
  2. 他是內部DSL届囚,語法兼容;不完全獨立是尖,但保留了獨立語法DSL優(yōu)點意系;

解決DSL的一些問題;

2. 帶接收者的Lambda

帶接收者的Lambda是Kotlin的一大特性饺汹,可以使用一個結構來構建API,擁有結構是區(qū)分DSL與普通API的關鍵特征蛔添;

我們來看下面3個函數(shù)

2.1 buildString、with 和 apply 函數(shù)

buildString函數(shù)

```
val abc = buildString {
        for (alpha in 'A'..'Z') {
            append(alpha)
        }
    }
    println(abc)
```

with函數(shù)

```
 val s = with(sb) {
        for(a in 'a'..'z') {
            append(a)
        }
        this.toString()
    }
  println(s)
```

apply 函數(shù)

```
val l = StringBuilder().apply {
        for(a in 'a'..'z') {
            append(a)
        }
    }.toString()
println(l)
```

一起過一下他們的方法簽名;

2.2 帶接收者的lambda和擴展函數(shù)類型

來創(chuàng)建一個自己的 mybuildString 函數(shù)

```
fun mybuildString(action: (StringBuilder) -> Unit) : String {
    val sb = StringBuilder()
    action(sb)
    return sb.toString()
}
fun main(args: Array<String>) {
    // 調用的使用需要it
    val s = mybuildString {
        it.append("Hello")
        it.append("World")
    }
    println(s)
}
```

很好理解上面的代碼迎瞧,但是調用時候夸溶,需要傳it,不夠簡潔,我們來改一下:

需要將lambda轉換成帶接收者的lambda

將接收者的特殊狀態(tài)賦予lambda參數(shù)的一個凶硅,這樣就可以不需要任何修飾符就能直接調用他的成員缝裁;

```
// 定義帶接收者的lambda的類型參數(shù)
fun mybuildString2(action: StringBuilder.() -> Unit) : String {
    val sb = StringBuilder()
    sb.action()
    return sb.toString()
}
```

這里傳遞的是一個帶接收者的lambda作為參數(shù)(匿名擴展函數(shù)),可以去掉lambda函數(shù)體中的 it

變化:用擴展函數(shù)類型取代了普通函數(shù)類型來聲明參數(shù)足绅;
StringBuilder.() -> Unit 替代了 (StringBuilder) -> Unit,

這個特殊的類型(StringBuilder)叫做接收者類型捷绑,傳遞給lambda的這個類型的值叫做接收者對象

```
接收者類型   參數(shù)類型     返回類型
String.     (Int,Int) -> Unit   

```

上面的是一個擴展函數(shù)類型,接收者是String氢妈;
實際上,一個擴展函數(shù)類型粹污,描述了一個可以被當做擴展函數(shù)來調用的代碼塊

不是將參數(shù)傳給lambda,而是像調用擴展函數(shù)那樣調用lambda; 上面的的 action 不是String類的方法首量,他是一個函數(shù)類型的參數(shù)壮吩,但是可以調用擴展函數(shù)一樣的語法調用他;

看一下圖:

image.png

函數(shù)的實參(帶lambda的接收者),對應于擴展函數(shù)類型的形參(action);lambda函數(shù)體被調用的時候蕾总,接收者(sb)變成了一個隱士的接收者(this

用變量來保存帶接收者的lambda

```
val cc : StringBuilder.() -> Unit = {
    this.append("better")
    this.append("wolrd")
}

fun main(args: Array<String>) {
    val sb = StringBuilder("good luck ")
    sb.cc()
    println(sb)
}
```

lambda帶接收者的lambda 要確定一個lambda是否有接收者粥航,需要看lambda被傳遞給了什么函數(shù) 函數(shù)的簽名會說明lambda是否有接收者,及其接收者的類型

我們再來一起看看 apply, with 函數(shù)

3. 在HTML構建器中使用帶接收者的lambda

用于HTML的Kotlin DSL稱為HTML構建器(類型安全構建器)
構建器在Groovy中很流行(gradle)生百,Kotlin也使用了递雀;

但gradle中,沒有提示蚀浆,因為是動態(tài)的缀程,可以瞎寫,
但Kotlin不能瞎寫市俊,編譯就報錯了杨凑,更安全了;

3.1 構建一個HTML

```
fun createSimpleTable() = createHTML().
    table {
        tr {
            td {""}     
        }
    }
```

以上代碼摆昧,table撩满、tr、td 都是函數(shù)绅你,是高階函數(shù)伺帘,接收帶 接收者的lambda為參數(shù);

在傳遞給table函數(shù)的lambda中忌锯,可用tr來創(chuàng)建<tr>標簽伪嫁,而在lambda之外,tr函數(shù)無法被解析偶垮;td類似张咳;

每一個代碼塊中的命名解析上下文是由每一個lambda的接收者的類型定義的帝洪;如:

  1. 傳遞給table的lambda的接收者類型是Table,其定義tr函數(shù)脚猾;
  2. 同理葱峡,tr 函數(shù)的lambda的接收者是Tr;

代碼

```
open class Tag

class TABLE : Tag() {
    fun tr(init:  TR.() -> Unit) {
        TR().init()
    }
}

class TR : Tag() {
    fun td(init: TD.() -> Unit) {

    }
}

class TD:Tag()

fun table(init: TABLE.() -> Unit) = TABLE().init()

```

調用代碼

```
table {
        tr {
            (this@tr).td {

            }
        }
    }
```

完整實現(xiàn)

```
open abstract class MyTag(val name:String) {
    protected val children = mutableListOf<MyTag>()
    override fun toString(): String {
        return "<$name>${children.joinToString("")}</$name>"        // <td></td>
    }
}
class MyTd:MyTag("td")
class MyTr:MyTag("tr") {
    fun td(init: MyTd.() -> Unit) {
        val td = MyTd()
        td.init()
        children.add(td)
    }
}
class MyTable:MyTag("table") {
    fun tr(init: MyTr.() -> Unit) {
        val tr = MyTr()
        tr.init()
        children.add(tr)
    }
}

fun table(init: MyTable.() -> Unit) : MyTable {
    val table = MyTable()
    table.init()
    return table
}

fun main(args: Array<String>) {
    val ta = table {
        tr {
            td {  }
            td {  }
        }
    }
    println(ta)
}
```
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末婚陪,一起剝皮案震驚了整個濱河市族沃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌泌参,老刑警劉巖脆淹,帶你破解...
    沈念sama閱讀 222,464評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異沽一,居然都是意外死亡盖溺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,033評論 3 399
  • 文/潘曉璐 我一進店門铣缠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來烘嘱,“玉大人,你說我怎么就攤上這事蝗蛙∮ィ” “怎么了?”我有些...
    開封第一講書人閱讀 169,078評論 0 362
  • 文/不壞的土叔 我叫張陵捡硅,是天一觀的道長哮内。 經(jīng)常有香客問我,道長壮韭,這世上最難降的妖魔是什么北发? 我笑而不...
    開封第一講書人閱讀 59,979評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮喷屋,結果婚禮上琳拨,老公的妹妹穿的比我還像新娘。我一直安慰自己屯曹,他們只是感情好狱庇,可當我...
    茶點故事閱讀 69,001評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著恶耽,像睡著了一般密任。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驳棱,一...
    開封第一講書人閱讀 52,584評論 1 312
  • 那天,我揣著相機與錄音农曲,去河邊找鬼社搅。 笑死驻债,一個胖子當著我的面吹牛,可吹牛的內容都是我干的形葬。 我是一名探鬼主播合呐,決...
    沈念sama閱讀 41,085評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笙以!你這毒婦竟也來了淌实?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,023評論 0 277
  • 序言:老撾萬榮一對情侶失蹤猖腕,失蹤者是張志新(化名)和其女友劉穎拆祈,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體倘感,經(jīng)...
    沈念sama閱讀 46,555評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡放坏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,626評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了老玛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片淤年。...
    茶點故事閱讀 40,769評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜡豹,靈堂內的尸體忽然破棺而出麸粮,到底是詐尸還是另有隱情,我是刑警寧澤镜廉,帶...
    沈念sama閱讀 36,439評論 5 351
  • 正文 年R本政府宣布弄诲,位于F島的核電站,受9級特大地震影響桨吊,放射性物質發(fā)生泄漏威根。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,115評論 3 335
  • 文/蒙蒙 一视乐、第九天 我趴在偏房一處隱蔽的房頂上張望洛搀。 院中可真熱鬧,春花似錦佑淀、人聲如沸留美。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,601評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谎砾。三九已至,卻和暖如春捧颅,著一層夾襖步出監(jiān)牢的瞬間景图,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,702評論 1 274
  • 我被黑心中介騙來泰國打工碉哑, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挚币,地道東北人亮蒋。 一個月前我還...
    沈念sama閱讀 49,191評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像妆毕,于是被迫代替她去往敵國和親慎玖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,781評論 2 361

推薦閱讀更多精彩內容