欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

golang中interface接口怎么用-創(chuàng)新互聯(lián)

這篇文章給大家分享的是有關(guān)golang中interface接口怎么用的內(nèi)容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

為化德等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計制作服務(wù),及化德網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站設(shè)計制作、成都網(wǎng)站制作、化德網(wǎng)站設(shè)計,以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

一 接口介紹


如果說gorountine和channel是支撐起Go語言的并發(fā)模型的基石,讓Go語言在如今集群化與多核化的時代成為一道亮麗的風(fēng)景,那么接口是Go語言整個類型系列的基石,讓Go語言在基礎(chǔ)編程哲學(xué)的探索上達(dá)到前所未有的高度。Go語言在編程哲學(xué)上是變革派,而不是改良派。這不是因為Go語言有g(shù)orountine和channel,而更重要的是因為Go語言的類型系統(tǒng),更是因為Go語言的接口。Go語言的編程哲學(xué)因為有接口而趨于完美。C++,Java 使用"侵入式"接口,主要表現(xiàn)在實現(xiàn)類需要明確聲明自己實現(xiàn)了某個接口。這種強制性的接口繼承方式是面向?qū)ο缶幊趟枷氚l(fā)展過程中一個遭受相當(dāng)多質(zhì)疑的特性。Go語言采用的是“非侵入式接口",Go語言的接口有其獨到之處:只要類型T的公開方法完全滿足接口I的要求,就可以把類型T的對象用在需要接口I的地方,所謂類型T的公開方法完全滿足接口I的要求,也即是類型T實現(xiàn)了接口I所規(guī)定的一組成員。這種做法的學(xué)名叫做Structural Typing,有人也把它看作是一種靜態(tài)的Duck Typing。


要這個值實現(xiàn)了接口的方法。

type Reader interface { 
 Read(p []byte) (n int, err os.Error) 
} 
 
// Writer 是包裹了基礎(chǔ) Write 方法的接口。 
type Writer interface { 
 Write(p []byte) (n int, err os.Error) 
} 
 
var r io.Reader 
r = os.Stdin 
r = bufio.NewReader(r) 
r = new(bytes.Buffer)

有一個事情是一定要明確的,不論 r 保存了什么值,r 的類型總是io.Reader ,Go 是靜態(tài)類型,而 r 的靜態(tài)類型是 io.Reader。接口類型的一個極端重要的例子是空接口interface{},它表示空的方法集合,由于任何值都有零個或者多個方法,所以任何值都可以滿足它。也有人說 Go 的接口是動態(tài)類型的,不過這是一種誤解。 它們是靜態(tài)類型的:接口類型的變量總是有著相同的靜態(tài)類型,這個值總是滿足空接口,只是存儲在接口變量中的值運行時可能被改變。對于所有這些都必須嚴(yán)謹(jǐn)?shù)膶Υ?,因為反射和接口密切相關(guān)。


二  接口類型內(nèi)存布局


在類型中有一個重要的類別就是接口類型,表達(dá)了固定的一個方法集合。一個接口變量可以存儲任意實際值(非接口),只要這個值實現(xiàn)了接口的方法。interface在內(nèi)存上實際由兩個成員組成,如下圖,tab指向虛表,data則指向?qū)嶋H引用的數(shù)據(jù)。虛表描繪了實際的類型信息及該接口所需要的方法集。

type Stringer interface { 
 String() string 
} 
 
type Binary uint64 
 
func (i Binary) String() string { 
 return strconv.FormatUint(i.Get(), 2) 
} 
 
func (i Binary) Get() uint64 { 
 return uint64(i) 
} 
 
func main() { 
 var b Binary = 32 
 s := Stringer(b) 
 fmt.Print(s.String()) 
}

golang中interface接口怎么用


觀察itable的結(jié)構(gòu),首先是描述type信息的一些元數(shù)據(jù),然后是滿足Stringger接口的函數(shù)指針列表(注意,這里不是實際類型Binary的函數(shù)指針集哦)。因此我們?nèi)绻ㄟ^接口進行函數(shù)調(diào)用,實際的操作其實就是s.tab->fun[0](s.data)。是不是和C++的虛表很像?但是他們有本質(zhì)的區(qū)別。先看C++,它為每個類創(chuàng)建了一個方法集即虛表,當(dāng)子類重寫父類的虛函數(shù)時,就將表中的相應(yīng)函數(shù)指針改為子類自己實現(xiàn)的函數(shù),如果沒有則指向父類的實現(xiàn),當(dāng)面臨多繼承時,C++對象結(jié)構(gòu)里就會存在多個虛表指針,每個虛表指針指向該方法集的不同部分。我們再來看golang的實現(xiàn)方式,同C++一樣,golang也為每種類型創(chuàng)建了一個方法集,不同的是接口的虛表是在運行時專門生成的,而c++的虛表是在編譯時生成的(但是c++虛函數(shù)表表現(xiàn)出的多態(tài)是在運行時決定的).例如,當(dāng)例子中當(dāng)首次遇見s := Stringer(b)這樣的語句時,golang會生成Stringer接口對應(yīng)于Binary類型的虛表,并將其緩存。那么為什么go不采用c++的方式來實現(xiàn)呢?這根c++和golang的對象內(nèi)存布局是有關(guān)系的。


首先c++的動態(tài)多態(tài)是以繼承為基礎(chǔ)的,在對象構(gòu)造初始化的時首先會初始化父類,其次是子類,也就是說一個對象的內(nèi)存布局是虛表,父類部分,子類部分(編譯器不同可能會有差異),當(dāng)一個父類指針指向子類時,會發(fā)生內(nèi)存的截斷,截斷子類部分(內(nèi)存地址偏移),但是此時子類的虛表中的函數(shù)指針實際上還是指向了自己的實現(xiàn),所以此時的指針才會調(diào)用到子類的虛函數(shù),如果不是虛函數(shù),因為內(nèi)存已經(jīng)截斷沒有子類的非虛函數(shù)信息了,所以只能調(diào)用父類的了,這種繼承關(guān)系讓c++的虛表的初始化非常清晰,在一個對象初始化時先調(diào)用父類的構(gòu)造此時虛表跟父類是一樣的,接下來初始化子類,此時編譯器就會去識別子類有沒有覆蓋父類的虛函數(shù),如果有則虛表中相應(yīng)的函數(shù)指針改成自己的虛函數(shù)實現(xiàn)指針。


那么go有什么不同呢,首先我們很清楚go是沒有嚴(yán)格意義上的繼承的,go的接口不存在繼承關(guān)系,只要實現(xiàn)了接口定義的方法都可以成為接口類型,這給go的虛表初始化帶來很大的麻煩,到底有多少類型實現(xiàn)了這個接口,一個類型到底實現(xiàn)了多少接口這讓編譯器很confused。舉個例子,某個類型有m個方法,某接口有n個方法,則很容易知道這種判定的時間復(fù)雜度為O(mXn),不過可以使用預(yù)先排序的方式進行優(yōu)化,實際的時間復(fù)雜度為O(m+n)這樣看來其實還行那為什么要在運行時生成虛表呢,這不是會拖慢程序的運行速度嗎,注意我們這里是某個類型,某個接口,是1對1的關(guān)系,如果有n個類型,n個接口呢,編譯器難道要把之間所有的關(guān)系都理清嗎?退一步說就算編譯器任勞任怨把這事干了,可是你在寫過程中你本來就不想實現(xiàn)那個接口,而你無意中給這個類型實現(xiàn)的方法中包含了某些接口的方法,你根本不需要這個接口(況且go的接口機制會導(dǎo)致很多這種無意義的接口實現(xiàn)),你欺負(fù)編譯器就行了,這也太欺負(fù)人了吧。如果我們放到運行時呢,我們只要在需要接口的去分析一下類型是否實現(xiàn)了接口的所有方法就行了很簡單的一件事。


三 空接口


接口類型的一個極端重要的例子是空接口:interface{} ,它表示空的方法集合,由于任何值都有零個或者多個方法,所以任何值都可以滿足它。 注意,[]T不能直接賦值給[]interface{}

//t := []int{1, 2, 3, 4} wrong 
//var s []interface{} = t 
t := []int{1, 2, 3, 4} //right 
s := make([]interface{}, len(t)) 
for i, v := range t { 
 s[i] = v 
}
str, ok := value.(string) 
if ok { 
 fmt.Printf("string value is: %q\n", str) 
} else { 
 fmt.Printf("value is not a string\n") 
}

在Go語言中,我們可以使用type switch語句查詢接口變量的真實數(shù)據(jù)類型,語法如下:

type Stringer interface { 
  String() string 
} 
 
var value interface{} // Value provided by caller. 
switch str := value.(type) { 
case string: 
  return str //type of str is string 
case Stringer: //type of str is Stringer 
  return str.String() 
}

也可以使用“comma, ok”的習(xí)慣用法來安全地測試值是否為一個字符串:

str, ok := value.(string) 
if ok { 
  fmt.Printf("string value is: %q\n", str) 
} else { 
  fmt.Printf("value is not a string\n") 
}

四 接口賦值

package main 
 
import ( 
"fmt" 
) 
 
type LesssAdder interface { 
  Less(b Integer) bool 
  Add(b Integer) 
} 
 
type Integer int 
 
func (a Integer) Less(b Integer) bool { 
  return a < b 
} 
 
func (a *Integer) Add(b Integer) { 
  *a += b 
} 
 
func main() { 
 
  var a Integer = 1 
  var b LesssAdder = &a 
  fmt.Println(b) 
 
  //var c LesssAdder = a 
  //Error:Integer does not implement LesssAdder  
  //(Add method has pointer receiver) 
}

go語言可以根據(jù)下面的函數(shù):

func (a Integer) Less(b Integer) bool

自動生成一個新的Less()方法

func (a *Integer) Less(b Integer) bool

這樣,類型*Integer就既存在Less()方法,也存在Add()方法,滿足LessAdder接口。 而根據(jù)

func (a *Integer) Add(b Integer)

這個函數(shù)無法生成以下成員方法:

func(a Integer) Add(b Integer) { 
  (&a).Add(b) 
}

因為(&a).Add()改變的只是函數(shù)參數(shù)a,對外部實際要操作的對象并無影響(值傳遞),這不符合用戶的預(yù)期。所以Go語言不會自動為其生成該函數(shù)。因此類型Integer只存在Less()方法,缺少Add()方法,不滿足LessAddr接口。(可以這樣去理解:指針類型的對象函數(shù)是可讀可寫的,非指針類型的對象函數(shù)是只讀的)將一個接口賦值給另外一個接口 在Go語言中,只要兩個接口擁有相同的方法列表(次序不同不要緊),那么它們就等同的,可以相互賦值。 如果A接口的方法列表時接口B的方法列表的子集,那么接口B可以賦值給接口A,但是反過來是不行的,無法通過編譯。

五 接口查詢


接口查詢是否成功,要在運行期才能夠確定。他不像接口的賦值,編譯器只需要通過靜態(tài)類型檢查即可判斷賦值是否可行。

var file1 Writer = ...
if file5,ok := file1.(two.IStream);ok {
...
}

這個if語句檢查file1接口指向的對象實例是否實現(xiàn)了two.IStream接口,如果實現(xiàn)了,則執(zhí)行特定的代碼。

在Go語言中,你可以詢問它指向的對象是否是某個類型,比如,

var file1 Writer = ...
if file6,ok := file1.(*File);ok {
...
}

這個if語句判斷file1接口指向的對象實例是否是*File類型,如果是則執(zhí)行特定的代碼。

slice := make([]int, 0)
slice = append(slice, 1, 2, 3)

var I interface{} = slice


if res, ok := I.([]int);ok {
  fmt.Println(res) //[1 2 3]
}

這個if語句判斷接口I所指向的對象是否是[]int類型,如果是的話輸出切片中的元素。

func Sort(array interface{}, traveser Traveser) error {

  if array == nil {
    return errors.New("nil pointer")
  }
  var length int //數(shù)組的長度
  switch array.(type) {
  case []int:
    length = len(array.([]int))
  case []string:
    length = len(array.([]string))
  case []float32:
    length = len(array.([]float32))

  default:
    return errors.New("error type")
  }

  if length == 0 {
    return errors.New("len is zero.")
  }

  traveser(array)

  return nil
}

通過使用.(type)方法可以利用switch來判斷接口存儲的類型。

感謝各位的閱讀!關(guān)于“golang中interface接口怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,讓大家可以學(xué)到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

本文標(biāo)題:golang中interface接口怎么用-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://aaarwkj.com/article10/cojjdo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營銷推廣靜態(tài)網(wǎng)站、網(wǎng)站制作、企業(yè)建站、ChatGPT、標(biāo)簽優(yōu)化

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

手機網(wǎng)站建設(shè)
欧美国产日韩亚洲综合| 中文字幕在线日韩精品| 免费女同一区二区三区| 欧美一区二区三区蜜桃| 免费精品99久久久国产| 性感91美女白丝在线精品| 成人激情视频在线网页| 国产一区二区高清不卡| 国产三级国产精品国产专播| 天天日夜夜操人人干人人插| 国产一区二区三区不卡视频| 国产激情在线四五区观看| 国产黄片一区二区在线| 国产日产亚洲综合一区| 精品人妻中文av一区二区| 久久亚洲一区二区内射| 成人性生活黄色三级视频| 欧美国产日韩激情在线| 日韩女同性一区二区三区| 欧美久久久久综合一区| 久久精品少妇人妻视频| 麻豆国产传媒片在线观看| 精品国产一区二区日韩91| 国产a天堂一区二区专区| 亚洲欧美另类不卡专区| 亚洲精品一区二区三区不卡| 亚洲黄色片大奶子水多| 日本成人一区二区三区在线| 婷婷网色偷偷亚洲男人| 在线观看国产精品女主播户外麻豆| 雪白肥臀视频一区二区三区| 人妻中文字幕在线一二区| 精品人妻一区二区在线99| 福利成人午夜国产一区| 亚洲精品深夜福利视频| 日韩黄色精品中文视频| 日韩免费系列一区二区| 国产三级三级精品久久| 麻豆午夜视频免费在线观看| 亚洲国产欧美日韩一区| 在线看日本一区二区|