日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
讓我一起談?wù)凣o中接口

本文轉(zhuǎn)載自微信公眾號(hào)「光城」,作者lightcity。轉(zhuǎn)載本文請(qǐng)聯(lián)系光城公眾號(hào)。

創(chuàng)新互聯(lián)主要從事成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)青河,十多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專(zhuān)業(yè),歡迎來(lái)電咨詢(xún)建站服務(wù):028-86922220

1.接口

在Go中使用interface關(guān)鍵字聲明一個(gè)接口:

 
 
 
  1. type Shaper interface { 
  2.  Area() float64 
  3.  Perimeter() float64 

如果我們直接使用fmt庫(kù)進(jìn)行輸出,會(huì)得到什么結(jié)果呢?

 
 
 
  1. func main() { 
  2.  var s Shaper 
  3.  fmt.Println("value of s is ", s) 
  4.  fmt.Printf("type of s is %T\n", s) 

輸出:

 
 
 
  1. value of s is   
  2. type of s is  

在這里,引出接口的概念。接口有兩種類(lèi)型。接口的靜態(tài)類(lèi)型是接口本身,例如上述程序中的Shape。接口沒(méi)有靜態(tài)值,而是指向動(dòng)態(tài)值。

接口類(lèi)型的變量可以保存實(shí)現(xiàn)接口的類(lèi)型的值。該類(lèi)型的值成為接口的動(dòng)態(tài)值,并且該類(lèi)型成為接口的動(dòng)態(tài)類(lèi)型。

從上面的示例開(kāi)始,我們可以看到零值和接口的類(lèi)型為nil。這是因?yàn)?,此刻,我們已聲明?lèi)型Shaper的變量s,但未分配任何值。當(dāng)我們使用帶有接口參數(shù)的fmt包中的Println函數(shù)時(shí),它指向接口的動(dòng)態(tài)值,Printf功能中的%T語(yǔ)法是指動(dòng)態(tài)類(lèi)型的接口。實(shí)際上,接口靜態(tài)類(lèi)型是Shaper。

當(dāng)我們使用一個(gè)類(lèi)型去實(shí)現(xiàn)該接口后,會(huì)是什么效果。

 
 
 
  1. type Rect struct { 
  2.  width  float64 
  3.  height float64 
  4.  
  5. func (r Rect) Area() float64 { 
  6.  return r.width * r.height 
  7.  
  8. func (r Rect) Perimeter() float64 { 
  9.  return 2 * (r.width + r.height) 
  10.  
  11. // main 
  12. func main() { 
  13.  var s Shaper 
  14.  fmt.Println("value of s is ", s) 
  15.  fmt.Printf("type of s is %T\n", s) 
  16.  s = Rect{5.0, 4.0} 
  17.  r := Rect{5.0, 4.0} 
  18.  fmt.Printf("type of s is %T\n", s) 
  19.  fmt.Printf("value of s is %v\n", s) 
  20.  fmt.Printf("area of rect is %v\n", s.Area()) 
  21.  fmt.Println("s == r is", s == r) 

輸出:

 
 
 
  1. value of s is   
  2. type of s is  
  3. type of s is main.Rect 
  4. value of s is {5 4} 
  5. area of rect is 20 
  6. s == r is tru 

可以看到此時(shí)s變成了動(dòng)態(tài)類(lèi)型,存儲(chǔ)的是main.Rect,值變成了{(lán)5,4}。

有時(shí),動(dòng)態(tài)類(lèi)型的接口也稱(chēng)為具體類(lèi)型,因?yàn)楫?dāng)我們?cè)L問(wèn)接口類(lèi)型時(shí),它會(huì)返回其底層動(dòng)態(tài)值的類(lèi)型,并且其靜態(tài)類(lèi)型保持隱藏。

我們可以在s上調(diào)用Area方法,因?yàn)榻涌赟haper定義了Area方法,而s的具體類(lèi)型是Rect,它實(shí)現(xiàn)了Area方法。該方法將在接口保存的動(dòng)態(tài)值上被調(diào)用。

此外,我們可以看到我們可以使用s與r進(jìn)行比較,因?yàn)檫@兩個(gè)變量都保存相同的動(dòng)態(tài)類(lèi)型(Rect類(lèi)型的結(jié)構(gòu))和動(dòng)態(tài)值{5 4}。

我們接著使用圓來(lái)實(shí)現(xiàn)該接口:

 
 
 
  1. type Circle struct { 
  2.  radius float64 
  3.  
  4. func (c Circle) Area() float64 { 
  5.  return 3.14 * c.radius * c.radius 
  6.  
  7. func (c Circle) Perimeter() float64 { 
  8.  return 2 * 3.14 * c.radius 
  9. // main 
  10. s = Circle{10} 
  11. fmt.Printf("type of s is %T\n", s) 
  12. fmt.Printf("value of s is %v\n", s) 
  13. fmt.Printf("area of rect is %v\n", s.Area()) 

此時(shí)輸出:

 
 
 
  1. type of s is main.Circle 
  2. value of s is {10} 
  3. area of rect is 314 

這里進(jìn)一步理解了接口保存的動(dòng)態(tài)類(lèi)型。從切片角度出發(fā),可以說(shuō),接口也以類(lèi)似的方式工作,即動(dòng)態(tài)保存對(duì)底層類(lèi)型的引用。

當(dāng)我們刪除掉Perimeter的實(shí)現(xiàn),可以看到如下報(bào)錯(cuò)結(jié)果。

 
 
 
  1. ./rect.go:34:4: cannot use Rect{...} (type Rect) as type Shaper in assignment: 
  2. Rect does not implement Shaper (missing Perimeter method) 

從上面的錯(cuò)誤應(yīng)該是顯而易見(jiàn)的,為了成功實(shí)現(xiàn)接口,需要實(shí)現(xiàn)與完全簽名的接口聲明的所有方法。

2.空接口

當(dāng)一個(gè)接口沒(méi)有任何方法時(shí),它被稱(chēng)為空接口。這由接口{}表示。因?yàn)榭战涌跊](méi)有方法,所以所有類(lèi)型都隱式地實(shí)現(xiàn)了這個(gè)接口。

空接口的作用之一在于:函數(shù)可以接收多個(gè)不同類(lèi)型參數(shù)。

例如:fmt的Println函數(shù)。

 
 
 
  1. func Println(a ...interface{}) (n int, err error) 

Println是一個(gè)可變函數(shù),它接受interface{}類(lèi)型的參數(shù)。

例如:

 
 
 
  1. type MyString string 
  2.  
  3. func explain(i interface{}) { 
  4.  fmt.Printf("type: %T, value: %v\n", i, i) 
  5. // main 
  6. s := MyString("hello") 
  7. explain(s) 
  8. r := Rect{1, 2} 
  9. explain(r) 

輸出:

 
 
 
  1. type: inter.MyString, value: hello 
  2. type: inter.Rect, value: {1 2} 

可以看到空接口的類(lèi)型與值是動(dòng)態(tài)的。

3.多個(gè)接口

在下面的程序中,我們用Area方法創(chuàng)建了Shape接口,用Volume方法創(chuàng)建了Object接口。因?yàn)榻Y(jié)構(gòu)類(lèi)型Cube實(shí)現(xiàn)了這兩個(gè)方法,所以它實(shí)現(xiàn)了這兩個(gè)接口。因此,我們可以將結(jié)構(gòu)類(lèi)型Cube的值賦給類(lèi)型為Shape或Object的變量。

 
 
 
  1. type IShape interface { 
  2.  Area() float64 
  3.  
  4. type Object interface { 
  5.  Volume() float64 
  6.  
  7. type Cube struct { 
  8.  side float64 
  9.  
  10. func (c Cube) Area() float64 { 
  11.  return 6 * c.side * c.side 
  12.  
  13. func (c Cube) Volume() float64 { 
  14.  return c.side * c.side * c.side 
  15. // main 
  16. c := Cube{3} 
  17. var s IShape = c 
  18. var o Object = c 
  19. fmt.Println("area is", s.Area()) 
  20. fmt.Println("Volume is", o.Volume()) 

這種調(diào)用是沒(méi)有問(wèn)題的,調(diào)用各自動(dòng)態(tài)類(lèi)型的方法。

那如果是這樣呢?

 
 
 
  1. fmt.Println("area of s of interface type IShape is", s.Volume()) 
  2. fmt.Println("volume of o of interface type Object is", o.Area()) 

輸出:

 
 
 
  1. s.Volume undefined (type Shape has no field or method Volume) 
  2. o.Area undefined (type Object has no field or method Area) 

這個(gè)程序無(wú)法編譯,因?yàn)閟的靜態(tài)類(lèi)型是IShape,而o的靜態(tài)類(lèi)型是Object。因?yàn)镮Shape沒(méi)有定義Volume方法,Object也沒(méi)有定義Area方法,所以我們得到了上面的錯(cuò)誤。

要使其工作,我們需要以某種方式提取這些接口的動(dòng)態(tài)值,這是一個(gè)立方體類(lèi)型的結(jié)構(gòu)體,立方體實(shí)現(xiàn)了這些方法。這可以使用類(lèi)型斷言來(lái)完成。

4.類(lèi)型斷言

我們可以通過(guò)i.(Type)確定接口i的底層動(dòng)態(tài)值,Go將檢查i的動(dòng)態(tài)類(lèi)型是否與type相同,并返回可能的動(dòng)態(tài)值。

 
 
 
  1. var s1 IShape = Cube{3} 
  2. c1 := s1.(Cube) 
  3. fmt.Println("area of s of interface type IShape is", c1.Volume()) 
  4. fmt.Println("volume of o of interface type Object is", c1.Area()) 

這樣便可以正常工作了。

如果IShape沒(méi)有存儲(chǔ)Cube類(lèi)型,且Cube沒(méi)有實(shí)現(xiàn)IShape,那么報(bào)錯(cuò):

 
 
 
  1. impossible type assertion: 
  2. Cube does not implement IShape (missing Area method) 

如果IShape沒(méi)有存儲(chǔ)Cube類(lèi)型,且Cube實(shí)現(xiàn)Shape,那么報(bào)錯(cuò):

 
 
 
  1. panic: interface conversion: inter.IShape is nil, not inter.Cub 

幸運(yùn)的是,語(yǔ)法中還有另一個(gè)返回值:

 
 
 
  1. value, ok := i.(Type) 

在上面的語(yǔ)法中,如果i有具體的type類(lèi)型或type的動(dòng)態(tài)值,我們可以使用ok變量來(lái)檢查。如果不是,那么ok將為假,value將為T(mén)ype的零值(nil)。

此外,使用類(lèi)型斷言可以檢查該接口的動(dòng)態(tài)類(lèi)型是否實(shí)現(xiàn)了其他接口,就像前面的IShape的動(dòng)態(tài)類(lèi)型是Cube,它實(shí)現(xiàn)了IShape、Object接口,如下例子:

 
 
 
  1. vaule1, ok1 := s1.(Object) 
  2. value2, ok2 := s1.(Skin) 
  3. fmt.Printf("IShape s的動(dòng)態(tài)類(lèi)型值是: %v, 該動(dòng)態(tài)類(lèi)型是否實(shí)現(xiàn)了Object接口: %v\n", vaule1, ok1) 
  4. fmt.Printf("IShape s的動(dòng)態(tài)類(lèi)型值是: %v, 該動(dòng)態(tài)類(lèi)型是否實(shí)現(xiàn)了Skin接口: %v\n", value2, ok2) 

輸出:

 
 
 
  1. IShape s的動(dòng)態(tài)類(lèi)型值是: {3}, 該動(dòng)態(tài)類(lèi)型是否實(shí)現(xiàn)了Object接口: true 
  2. IShape s的動(dòng)態(tài)類(lèi)型值是: , 該動(dòng)態(tài)類(lèi)型是否實(shí)現(xiàn)了Skin接口: false 

類(lèi)型斷言不僅用于檢查接口是否具有某個(gè)給定類(lèi)型的具體值,而且還用于將接口類(lèi)型的給定變量轉(zhuǎn)換為不同的接口類(lèi)型。

5.類(lèi)型Switch

在前面的空接口中,我們知道將一個(gè)空接口作為函數(shù)參數(shù),那么該函數(shù)可以接受任意類(lèi)型,那如果我有一個(gè)需求是:當(dāng)傳遞的數(shù)據(jù)類(lèi)型是字符串時(shí),要求全部變?yōu)榇髮?xiě),其他類(lèi)型不進(jìn)行操作?

針對(duì)這樣的需求,我們可以采用Type Switch,即:i.(type)+switch。

 
 
 
  1. func switchProcess(i interface{}) { 
  2.  switch i.(type) { 
  3.  case string: 
  4.   fmt.Println("process string") 
  5.  case int: 
  6.   fmt.Println("process int") 
  7.  default: 
  8.   fmt.Printf("type is %T\n", i) 
  9.  } 

輸出:

 
 
 
  1. process int 
  2. process string 

6.嵌入接口

在Go中,一個(gè)接口不能實(shí)現(xiàn)或擴(kuò)展其他接口,但我們可以通過(guò)合并兩個(gè)或多個(gè)接口來(lái)創(chuàng)建一個(gè)新的接口。

例如:

這里使用Runner與Eater兩個(gè)接口,組合成了一個(gè)新接口RunEater,該接口為Embedding interfaces。

 
 
 
  1. type Runner interface { 
  2.  run() string 
  3. type Eater interface { 
  4.  eat() string 
  5.  
  6. type RunEater interface { 
  7.  Runner 
  8.  Eater 
  9.  
  10. type Dog struct { 
  11.  age int 
  12.  
  13. func (d Dog) run() string { 
  14.  return "run" 
  15.  
  16. func (d Dog) eat() string { 
  17.  return "eat" 
  18.  
  19. // main 
  20. d := Dog{10} 
  21. var re RunEater = d 
  22. var r Runner = d 
  23. var e Eater = d 
  24. fmt.Printf("RunnEater dynamic type: %T, value: %v\n", re, re) 
  25. fmt.Printf("Runn dynamic type: %T, value: %v\n", r, r) 
  26. fmt.Printf("Eater dynamic type: %T, value: %v\n", e, e) 

輸出:

 
 
 
  1. RunnEater dynamic type: inter.Dog, value: {10} 
  2. Runn dynamic type: inter.Dog, value: {10} 
  3. Eater dynamic type: inter.Dog, value: {10} 

7.接口比較

如果基礎(chǔ)動(dòng)態(tài)值為nil,則兩個(gè)接口總是相等的,這意味著兩個(gè)nil接口總是相等的,因此== operation返回true。

 
 
 
  1. var a, b interface{} 
  2. fmt.Println( a == b ) // true 

如果這些接口不是nil,那么它們的動(dòng)態(tài)類(lèi)型(具體值的類(lèi)型)應(yīng)該相同,具體值應(yīng)該相等。

如果接口的動(dòng)態(tài)類(lèi)型不具有可比性,例如slice、map、function,或者接口的具體值是包含這些不可比較性值的復(fù)雜數(shù)據(jù)結(jié)構(gòu),如切片或數(shù)組,則==或!=操作將導(dǎo)致運(yùn)行時(shí)panic。

學(xué)習(xí)自https://medium.com/rungo/interfaces-in-go-ab1601159b3a


文章題目:讓我一起談?wù)凣o中接口
網(wǎng)頁(yè)網(wǎng)址:http://www.dlmjj.cn/article/dpghsho.html