<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      kotlin更多語(yǔ)言結(jié)構(gòu)——>類型安全的構(gòu)建器

        通過使用命名得當(dāng)?shù)暮瘮?shù)作為構(gòu)建器,結(jié)合帶有接收者的函數(shù)字面值,可以在 Kotlin 中創(chuàng)建類型安全、靜態(tài)類型 的構(gòu)建器

        類型安全的構(gòu)建器可以創(chuàng)建基于 Kotlin 的適用于采用半聲明方式構(gòu)建復(fù)雜層次數(shù)據(jù)結(jié)構(gòu)領(lǐng)域?qū)S谜Z(yǔ)言(DSL)。 以下是構(gòu)建器的一些示例應(yīng)用場(chǎng)景:

          — 使用 Kotlin 代碼生成標(biāo)記語(yǔ)言,例如 HTML 或 XML;

          — 以編程方式布局UI組件:Anko;
          — 為Web服務(wù)器配置路由:Ktor。

       

       

      一個(gè)類型安全的構(gòu)建器示例

        考慮下面的代碼

      import com.example.html.* // 參?下文聲明
      
      fun result() = html {
          head {
              title { +"XML encoding with Kotlin" }
          }
          body {
              h1 { +"XML encoding with Kotlin" }
              p { +"this format can be used as an alternative markup to XML" }
              // 一個(gè)具有屬性和文本內(nèi)容的元素
              a() { +"Kotlin" }
              // 混合的內(nèi)容 
              p {
                  +"This is some"
                  b { +"mixed" }
                  +"text. For more see the"
                  a() { +"Kotlin" }
                  +"project"
              }
              p { +"some text" }
              // 以下代碼生成的內(nèi)容 
              p {
                  for (arg in args) +arg
              }
          }
      }
      

        這是完全合法的 Kotlin 代碼。你可以在這里在線運(yùn)行上文代碼(修改它并在瀏覽器中運(yùn)行)

       

      實(shí)現(xiàn)原理

        讓我們來(lái)看看 Kotlin 中實(shí)現(xiàn)類型安全構(gòu)建器的機(jī)制。首先,我們需要定義我們想要構(gòu)建的模型,在本例中我們 需要建模 HTML 標(biāo)簽。用一些類就可以輕易完成。例如,HTML 是一個(gè)描述 <html> 標(biāo)簽的類,也就是說(shuō)它定 義了像 <head> 和 <body> 這樣的子標(biāo)簽。(參?下文它的聲明。)

        現(xiàn)在,讓我們回想下為什么我們可以在代碼中這樣寫

      html {
          // ......
      }
      

        html 實(shí)際上是一個(gè)函數(shù)調(diào)用,它接受一個(gè) lambda 表達(dá)式 作為參數(shù)。該函數(shù)定義如下

      fun html(init: HTML.() -> Unit): HTML { 
          val html = HTML()
          html.init()
          return html
      }
      

        這個(gè)函數(shù)接受一個(gè)名為 init 的參數(shù),該參數(shù)本身就是一個(gè)函數(shù)。該函數(shù)的類型是 HTML.() -> Unit,它是 一個(gè) 帶接收者的函數(shù)類型 。這意味著我們需要向函數(shù)傳遞一個(gè) HTML 類型的實(shí)例( 接收者 ),并且我們可以在 函數(shù)內(nèi)部調(diào)用該實(shí)例的成員。該接收者可以通過 this 關(guān)鍵字訪問

      html {
           this.head { ...... }
           this.body { ...... }
      }    
      

        (head 和 body 是 HTML 的成員函數(shù)。)

        現(xiàn)在,像往常一樣,this 可以省略掉了,我們得到的東西看起來(lái)已經(jīng)非常像一個(gè)構(gòu)建器了

      html {
          head { ...... }
          body { ...... } 
      }
      

        那么,這個(gè)調(diào)用做什么?讓我們看看上面定義的 html 函數(shù)的主體。它創(chuàng)建了一個(gè) HTML 的新實(shí)例,然后通過 調(diào)用作為參數(shù)傳入的函數(shù)來(lái)初始化它(在我們的示例中,歸結(jié)為在HTML實(shí)例上調(diào)用 head 和 body),然后返 回此實(shí)例。這正是構(gòu)建器所應(yīng)做的。

        HTML 類中的 head 和 body 函數(shù)的定義與 html 類似。唯一的區(qū)別是,它們將構(gòu)建的實(shí)例添加到包含 HTML 實(shí)例的 children 集合中

      fun head(init: Head.() -> Unit) : Head { 
          val head = Head()
          head.init()
          children.add(head)
          return head 
      }
      
      fun body(init: Body.() -> Unit) : Body { 
          val body = Body()
          body.init()
          children.add(body)
          return body 
      }
      

        實(shí)際上這兩個(gè)函數(shù)做同樣的事情,所以我們可以有一個(gè)泛型版本,initTag

      protected fun <T : Element> initTag(tag: T, init: T.() -> Unit): T {
          tag.init()
          children.add(tag)
          return tag 
      }
      

        所以,現(xiàn)在我們的函數(shù)很簡(jiǎn)單

      fun head(init: Head.() -> Unit) = initTag(Head(), init)
      
      fun body(init: Body.() -> Unit) = initTag(Body(), init)
      

        并且我們可以使用它們來(lái)構(gòu)建 <head> 和 <body> 標(biāo)簽。

        這里要討論的另一件事是如何向標(biāo)簽體中添加文本。在上例中我們這樣寫到

      html { 
          head {
              title {+"XML encoding with Kotlin"} 
          }
          // ......
      }
      

        所以基本上,我們只是把一個(gè)字符串放進(jìn)一個(gè)標(biāo)簽體內(nèi)部,但在它前面有一個(gè)小的 +,所以它是一個(gè)函數(shù)調(diào)用, 調(diào)用一個(gè)前綴 unaryPlus() 操作。該操作實(shí)際上是由一個(gè)擴(kuò)展函數(shù) unaryPlus() 定義的,該函數(shù)是TagWithText 抽象類(Title 的父類)的成員

      operator fun String.unaryPlus() {
          children.add(TextElement(this))
      }
      

        所以,在這里前綴 + 所做的事情是把一個(gè)字符串包裝到一個(gè) TextElement 實(shí)例中,并將其添加到 children 集合中,以使其成為標(biāo)簽樹的一個(gè)適當(dāng)?shù)牟糠帧?/p>

        所有這些都在上面構(gòu)建器示例頂部導(dǎo)入的包 com.example.html 中定義。在最后一節(jié)中,你可以閱讀這個(gè)包 的完整定義

       

      作用域控制:@DslMarke(r 自 1.1 起)

        使用 DSL 時(shí),可能會(huì)遇到上下文中可以調(diào)用太多函數(shù)的問題。我們可以調(diào)用 lambda 表達(dá)式內(nèi)部每個(gè)可用的隱式接收者的方法,因此得到一個(gè)不一致的結(jié)果,就像在另一個(gè) head 內(nèi)部的 head 標(biāo)記那樣
      html { 
          head {
              head {} // 應(yīng)該禁止 
           }
          // ......
      }
      

        在這個(gè)例子中,必須只有最近層的隱式接收者 this@head 的成員可用;head() 是外部接收者 this@html 的成員,所以調(diào)用它一定是非法的。

        為了解決這個(gè)問題,在 Kotlin 1.1 中引入了一種控制接收者作用域的特殊機(jī)制。

        為了使編譯器開始控制標(biāo)記,我們只是必須用相同的標(biāo)記注解來(lái)標(biāo)注在 DSL 中使用的所有接收者的類型。例如,對(duì)于 HTML 構(gòu)建器,我們聲明一個(gè)注解 @HTMLTagMarker

       @DslMarker
      annotation class HtmlTagMarker
      

        如果一個(gè)注解類使用 @DslMarker 注解標(biāo)注,那么該注解類稱為 DSL 標(biāo)記。

        在我們的 DSL 中,所有標(biāo)簽類都擴(kuò)展了相同的超類 Tag 。只需使用 @HtmlTagMarker 來(lái)標(biāo)注超類就足夠了,之后,Kotlin 編譯器會(huì)將所有繼承的類視為已標(biāo)注

       @HtmlTagMarker
      abstract class Tag(val name: String) { ...... }
      

        我們不必用 @HtmlTagMarker 標(biāo)注 HTML 或 Head 類,因?yàn)樗鼈兊某愐褬?biāo)注過

      class HTML() : Tag("html") { ...... } 
      class Head() : Tag("head") { ...... }
      

        在添加了這個(gè)注解之后,Kotlin 編譯器就知道哪些隱式接收者是同一個(gè) DSL 的一部分,并且只允許調(diào)用最近層 的接收者的成員

      html { 
          head {
              head { } // 錯(cuò)誤:外部接收者的成員
           }
          // ......
      }
      

        請(qǐng)注意,仍然可以調(diào)用外部接收者的成員,但是要做到這一點(diǎn),你必須明確指定這個(gè)接收者

      html { 
          head {
              this@html.head { } // 可能
           }
          // ......
      }
      

        

      com.example.html 包的完整定義

        這就是 com.example.html 包的定義(只有上面例子中使用的元素)。它構(gòu)建一個(gè) HTML 樹。代碼中大量使 用了擴(kuò)展函數(shù)和帶有接收者的 lambda 表達(dá)式。

        請(qǐng)注意,@DslMarker 注解在 Kotlin 1.1 起才可用

      package com.example.html
      interface Element {
          fun render(builder: StringBuilder, indent: String)
      }
      
      class TextElement(val text: String) : Element {
          override fun render(builder: StringBuilder, indent: String) {
              builder.append("$indent$text\n")
          }
      }
      
      @DslMarker
      annotation class HtmlTagMarker
      
      @HtmlTagMarker
      abstract class Tag(val name: String) : Element {
          val children = arrayListOf<Element>()
          val attributes = hashMapOf<String, String>()
          protected fun <T : Element> initTag(tag: T, init: T.() -> Unit): T {
              tag.init()
              children.add(tag)
              return tag
          }
      
          override fun render(builder: StringBuilder, indent: String) {
              builder.append("$indent<$name${renderAttributes()}>\n") for (c in children) {
                  c.render(builder, indent + " ")
              }
              builder.append("$indent</$name>\n")
          }
      
          private fun renderAttributes(): String {
              val builder = StringBuilder()
              for ((attr, value) in attributes) {
                  builder.append(" $attr=\"$value\"")
              }
              return builder.toString()
          }
      
          override fun toString(): String {
              val builder = StringBuilder() render (builder, "")
              return builder.toString()
          }
      }
      
      abstract class TagWithText(name: String) : Tag(name) {
          operator fun String.unaryPlus() {
              children.add(TextElement(this))
          }
      }
      
      class HTML : TagWithText("html") {
          fun head(init: Head.() -> Unit) = initTag(Head(), init)
          fun body(init: Body.() -> Unit) = initTag(Body(), init)
      }
      
      class Head : TagWithText("head") {
          fun title(init: Title.() -> Unit) = initTag(Title(), init)
      }
      
      class Title : TagWithText("title")
      abstract class BodyTag(name: String) : TagWithText(name) {
          fun b(init: B.() -> Unit) = initTag(B(), init)
          fun p(init: P.() -> Unit) = initTag(P(), init)
          fun h1(init: H1.() -> Unit) = initTag(H1(), init)
          fun a(href: String, init: A.() -> Unit) {
              val a = initTag(A(), init)
              a.href = href
          }
      }
      
      class Body : BodyTag("body")
      class B : BodyTag("b")
      class P : BodyTag("p")
      class H1 : BodyTag("h1")
      
      class A : BodyTag("a") {
          var href: String
              get() = attributes["href"]!!
              set(value) {
                  attributes["href"] = value
              }
      }
      
      fun html(init: HTML.() -> Unit): HTML {
          val html = HTML()
          html.init()
          return html
      }
      

        

       

       

       

       

       

      posted @ 2022-01-28 02:27  王世楨  閱讀(113)  評(píng)論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 亚洲香蕉视频天天爽| 亚洲久悠悠色悠在线播放| 狼人大伊人久久一区二区| 欧美人与性囗牲恔配| av偷拍亚洲一区二区三区| 亚洲综合久久国产一区二区 | 成人午夜大片免费看爽爽爽| 亚洲精品日韩在线观看| 波多野结衣久久一区二区| 中文字幕av国产精品| 亚洲精品一二三四区| 自拍视频在线观看成人| 精品国产AV无码一区二区三区| 7777精品久久久大香线蕉| 国产精品久久久久久久9999| XXXXXHD亚洲日本HD| 免费看久久妇女高潮a| 女人喷水高潮时的视频网站| 久久99热只有频精品8| 久久精品亚洲中文字幕无码网站 | 国产mv在线天堂mv免费观看| 久久久精品94久久精品| AV最新高清无码专区| 国产在线观看播放av| 伊人久久大香线焦av综合影院| 日本在线 | 中文| 深夜av免费在线观看| 国产一区二区三区综合视频| 成人免费无遮挡无码黄漫视频| 日本一区二区中文字幕久久| 干中文字幕| 又湿又紧又大又爽A视频男| 亚洲国产亚洲国产路线久久| 国产亚洲精品成人aa片新蒲金| 最新亚洲人成无码WWW| 91精品91久久久久久| 国产精品乱一区二区三区| 国产呦交精品免费视频| 亚洲精品无码日韩国产不卡av| 美女无遮挡免费视频网站| 久久精品中文字幕少妇|