<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 面向?qū)ο缶幊唐航涌谫x值

          共 5163字,需瀏覽 11分鐘

           ·

          2021-03-07 14:50

          一、接口賦值概述

          上篇教程中,學(xué)院君給大家介紹了 Go 接口的定義及實(shí)現(xiàn),和其他編程語(yǔ)言一樣,Go 接口不支持直接實(shí)例化,因?yàn)樗皇且粋€(gè)契約而已,只能通過具體的類來實(shí)現(xiàn)接口聲明的所有方法。不同之處在于,Go 接口支持賦值操作,從而快速實(shí)現(xiàn)接口與實(shí)現(xiàn)類的映射,與之相比,Java、PHP 要實(shí)現(xiàn)接口與實(shí)現(xiàn)類的映射,只能基于 IoC 容器通過依賴注入實(shí)現(xiàn),要復(fù)雜的多。

          接口賦值在 Go 語(yǔ)言中分為如下兩種情況:

          • 將實(shí)現(xiàn)接口的類實(shí)例賦值給接口;

          • 將一個(gè)接口賦值給另一個(gè)接口。

          下面我們通過代碼實(shí)例逐個(gè)介紹對(duì)應(yīng)的實(shí)現(xiàn)和注意事項(xiàng)。

          二、將類實(shí)例賦值給接口

          先看看將類實(shí)例賦值給接口,這要求該實(shí)例對(duì)應(yīng)的類實(shí)現(xiàn)了接口聲明的所有方法,這個(gè)是自然,否則也就不能算作實(shí)現(xiàn)該接口了。

          只包含值方法

          我們以之前為基本類型添加成員方法時(shí)定義過的 Integer 類型為例進(jìn)行演示:

          type Integer int

          // 加法運(yùn)算
          func (a Integer) Add(b Integer) Integer {
              return a + b
          }

          // 乘法運(yùn)算
          func (a Integer) Multiply(b Integer) Integer {
              return a * b
          }

          type Math interface {
              Add(i Integer) Integer
              Multiply(i Integer) Integer
          }

          按照 Go 語(yǔ)言的約定,Integer 類型實(shí)現(xiàn)了 Math 接口。然后我們可以這樣將 Integer 類型的實(shí)例 a 直接賦值給 Math 接口類型的變量 m

          var a Integer = 1 
          var m Math = a
          fmt.Println(m.Add(1))

          對(duì)于值方法而言,進(jìn)行接口賦值時(shí)傳遞 a 實(shí)例的指針引用也是可以的:

          var a Integer = 1 
          var m Math = &amp;a
          fmt.Println(m.Add(1))

          因?yàn)閷?duì)于非指針方法,Go 底層會(huì)自動(dòng)生成一個(gè)與之對(duì)應(yīng)的指針成員方法:

          func (a *Integer) Add(i Integer) Integer { 
              return (*a).Add(i) 
          }

          func (a *Integer) Multiply(i Integer) Integer { 
              return (*a).Multiply(i) 
          }

          包含指針方法

          不過如果 Integer 類型中包含了歸屬于指針的實(shí)現(xiàn)方法:

          type Integer int

          func (a *Integer) Add(b Integer) {
              *a = (*a) + b
          }

          func (a Integer) Multiply(b Integer) Integer {
              return a * b
          }

          type Math interface {
              Add(i Integer)
              Multiply(i Integer) Integer
          }

          那么在做接口賦值時(shí),就只能傳遞指針類型的變量了:

          var a Integer = 1
          var m Math = &amp;a
          m.Add(2)
          fmt.Printf("1 + 2 = %d\n", a)

          因?yàn)?Integer 類型不包含指針方法(參考前面介紹的值方法與指針方法區(qū)別),所以此時(shí)只有 *Integer 類型實(shí)現(xiàn)了 Math 接口,如果我們直接將 a 的值類型賦值給 m,編譯時(shí)會(huì)報(bào)錯(cuò):

          cannot use a (type Integer) as type Math in assignment:
              Integer does not implement Math (Add method has pointer receiver)

          綜上所述,如果 Integer 類中實(shí)現(xiàn)接口的成員方法都是值方法,則進(jìn)行接口賦值時(shí),傳遞類實(shí)例的值類型或者指針類型均可,否則只能傳遞指針類型實(shí)例,從代碼性能角度來說,值拷貝需要消耗更多的內(nèi)存空間,統(tǒng)一使用指針類型代碼性能會(huì)更好。

          三、將接口賦值給接口

          接下來,我們來看如何將一個(gè)接口賦值給另一個(gè)接口:在 Go 語(yǔ)言中,只要兩個(gè)接口擁有相同的方法列表(與順序無關(guān)),那么它們就是等同的,可以相互賦值。不過,這里有一個(gè)前提,那就是接口變量持有的是基于對(duì)應(yīng)實(shí)現(xiàn)類的實(shí)例值,所以接口與接口間的賦值是基于類實(shí)例與接口間的賦值的。

          完全對(duì)等

          下面我們來編寫對(duì)應(yīng)的示例代碼,這是第一個(gè)接口 Number1

          type Number1 interface {
              Equal(i intbool
              LessThan(i intbool
              MoreThan(i intbool
          }

          這是第二個(gè)接口 Number2

          type Number2 interface {
              Equal(i intbool
              MoreThan(i intbool
              LessThan(i intbool
          }

          這里我們定義了兩個(gè)接口,一個(gè)叫 Number1,一個(gè)叫 Number2,兩者都定義三個(gè)相同的方法,只是順序不同而已。在 Go 語(yǔ)言中,這兩個(gè)接口實(shí)際上并無區(qū)別,因?yàn)椋?/p>

          • 任何實(shí)現(xiàn)了 Number1 接口的類,也實(shí)現(xiàn)了 Number2;

          • 任何實(shí)現(xiàn)了 Number1 接口的類實(shí)例都可以賦值給 Number2,反之亦然;

          • 在任何地方使用 Number1 接口與使用 Number2 并無差異。

          接下來我們定義一個(gè)實(shí)現(xiàn)了這兩個(gè)接口的類 Number

          type Number int

          func (n Number) Equal(i int) bool {
              return int(n) == i
          }

          func (n Number) LessThan(i int) bool {
              return int(n) &lt; i
          }

          func (n Number) MoreThan(i int) bool {
              return int(n) > i
          }

          那么下面這些賦值代碼都是合法的,會(huì)編譯通過:

          var num1 Number = 1
          var num2 Number1 = num1 
          var num3 Number2 = num2

          方法子集

          此外,接口賦值并不要求兩個(gè)接口完全等價(jià)(方法完全相同)。如果接口 A 的方法列表是接口 B 的方法列表的子集,那么接口 B 也可以賦值給接口 A。例如,假設(shè) Number2 接口定義如下:

          type Number2 interface {
              Equal(i intbool
              MoreThan(i intbool
              LessThan(i intbool
              Add(i int)
          }

          要讓 Number 類繼續(xù)保持實(shí)現(xiàn)這兩個(gè)接口,需要在 Number 類定義中新增一個(gè) Add 方法實(shí)現(xiàn)(這里定義了一個(gè)指針方法):

          func (n *Number) Add(i int) {
              *n = *n + Number(i)
          }

          接下來,將上面的接口賦值語(yǔ)句改寫如下即可:

          var num1 Number = 1
          var num2 Number2 = &amp;num1
          var num3 Number1 = num2 

          這樣一來,就實(shí)現(xiàn)了接口賦值,但是反過來不行:

          var num1 Number = 1
          var num2 Number1 = &amp;num1
          var num3 Number2 = num2   // 這一段編譯出錯(cuò)

          因?yàn)?Number1 接口中沒有聲明 Add 方法,或者換句話說,實(shí)現(xiàn)了 Number2 接口的類肯定實(shí)現(xiàn)了 Number1,但是實(shí)現(xiàn)了 Number1 接口的類不一定實(shí)現(xiàn)了 Number2。這句話是不是似曾相識(shí)?沒錯(cuò),這一點(diǎn)和 Java、PHP 中子類實(shí)例可以直接賦值給父類變量,而父類實(shí)例不能直接賦值給子類變量有異曲同工之妙,我們?cè)趯W(xué)習(xí)新知識(shí)時(shí)要善于通過這種類比來降低學(xué)習(xí)成本,提高學(xué)習(xí)效率。

          (本文完)


          學(xué)習(xí)過程中有任何問題,可以通過下面的評(píng)論功能或加入「Go 語(yǔ)言研習(xí)社」與學(xué)院君討論:


          本系列教程首發(fā)在 geekr.dev,你可以點(diǎn)擊頁(yè)面左下角閱讀原文鏈接查看最新更新的教程。

          瀏覽 89
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩网站免费在线观看 | 家庭乱伦一区二区 | 韩国无码不卡 | 爱草逼爱草逼爱草逼爱草逼爱草逼爱草逼爱草逼 | 欧美黄色大片免费在线观看 |