日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

golang中的接口

發(fā)布時間:2025/6/15 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 golang中的接口 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

接口

在go中,接口是一個自定義類型
接口類型是一個抽象類型,他不會暴露他代表的對象的內(nèi)部值的結(jié)構(gòu)和這個對象支持的基礎(chǔ)操作的集合,他們只會展示出自己的方法.因此接口類型不能將他實例化

定義

type Humaner interface {sayHi() }
  • 接口命名習(xí)慣以er結(jié)尾
  • 接口只有方法聲明,沒有實現(xiàn),沒有數(shù)據(jù)字段
  • 接口可以匿名嵌入其他接口,或嵌入到結(jié)構(gòu)中

實現(xiàn)

//定義接口類型 type Humaner interface {sayHi() }type Student struct {name stringid int } //Student類型實現(xiàn)了這個方法 func (tmp *Student) sayHi() {fmt.Println(*tmp) }func main() {//定義接口類型的變量var i Humaner//只要實現(xiàn)了此接口方法的類型,那么這個類型的變量(接收者類型)就可以給i賦值s := &Student{"Mike", 666}//引用賦值i= s//調(diào)用實現(xiàn)者的方法i.sayHi() }

多態(tài)

//定義接口類型 type Humaner interface {sayHi() }type Student struct {name stringid int } //Student類型實現(xiàn)了這個方法 func (tmp *Student) sayHi() {fmt.Println(*tmp) }type Mystr string //MyStr實現(xiàn)了這個方法 func (tmp *Mystr) sayHi() {fmt.Println(*tmp) } //定義一個普通函數(shù),函數(shù)的參數(shù)為接口類型 //只有一個函數(shù),缺有不同表現(xiàn) func WhoSayHi(i Humaner) {i.sayHi() }func main() {s := &Student{"Mike", 666}t := &Student{"sds", 6556}var str Mystr = "HELLO"//要傳地址WhoSayHi(s)WhoSayHi(t)WhoSayHi(&str) }

接口的繼承

//定義接口類型 type Humaner interface { //子集sayHi() }type Person interface { //超集Humaner //繼承了sayHi()sing(lrc string) }type Student struct {name stringid int } //Student類型實現(xiàn)了這個方法 func (tmp *Student) sayHi() {fmt.Println(*tmp) }func (tmp *Student) sing(lrc string) {fmt.Println(*tmp) }func main() {//定義一個接口類型的變量var i Persons := &Student{"mike", 666}i = si.sayHi() //繼承過來的方法i.sing("abc") }

接口轉(zhuǎn)換

超級可以轉(zhuǎn)換為子集,反過來不可以

func main() {//定義一個接口類型的變量var i Personvar h Humaneri = h //不可以 }

空接口

空接口(interface{})不包含任何方法,所有類型都實現(xiàn)了空接口,因此空接口可以存儲任意類型的數(shù)值.有點類似于c語言的void *類型

//將int類型賦值給interface{} var v1 interface{} = 1 //將string類型賦值給interface{} var v2 interface{} = "abc" //將*interface{]類型賦值給interface{} var v3 interface{} = &v2 var v4 interface{} = struct {X int }{1} var v5 interface{} = struct {x int }{1}

當(dāng)函數(shù)可以接受任意的對象實例時,我們會將其聲明為interface{},最典型的例子就是標(biāo)準(zhǔn)庫fmt中PrintXXX系列的函數(shù)

func Printf(fmt string, args ...interface{}) //可變參數(shù)的空接口類型 func Println(args ...interface{}) func main() {var i interface{} = 1fmt.Println(i) }

動態(tài)類型

對于任何數(shù)據(jù)類型,只要它的方法集合中完全包含了一個接口的全部特征(即全部的方法),那么它就一定是這個接口的實現(xiàn)類型。比如下面這樣:

type Pet interface {SetName(name string)Name() stringCategory() string }

怎樣判定一個數(shù)據(jù)類型的某一個方法實現(xiàn)的就是某個接口類型中的某個方法呢?

這有兩個充分必要條件,一個是“兩個方法的簽名需要完全一致”,另一個是“兩個方法的名稱要一模一樣”。顯然,這比判斷一個函數(shù)是否實現(xiàn)了某個函數(shù)類型要更加嚴(yán)格一些。

如果你查閱了上篇文章附帶的最后一個示例的話,那么就一定會知道,雖然結(jié)構(gòu)體類型Cat不是Pet接口的實現(xiàn)類型,但它的指針類型*Cat卻是這個的實現(xiàn)類型。

我聲明的類型Dog附帶了 3 個方法。其中有 2 個值方法,分別是Name和Category,另外還有一個指針方法SetName。

這就意味著,Dog類型本身的方法集合中只包含了 2 個方法,也就是所有的值方法。而它的指針類型*Dog方法集合卻包含了 3 個方法,

也就是說,它擁有Dog類型附帶的所有值方法和指針方法。又由于這 3 個方法恰恰分別是Pet接口中某個方法的實現(xiàn),所以*Dog類型就成為了Pet接口的實現(xiàn)類型。

dog := Dog{"little pig"} var pet Pet = &dog

正因為如此,我可以聲明并初始化一個Dog類型的變量dog,然后把它的指針值賦給類型為Pet的變量pet。

這里有幾個名詞需要你先記住。對于一個接口類型的變量來說,例如上面的變量pet,我們賦給它的值可以被叫做它的實際值(也稱動態(tài)值),而該值的類型可以被叫做這個變量的實際類型(也稱動態(tài)類型)。

比如,我們把取址表達(dá)式&dog的結(jié)果值賦給了變量pet,這時這個結(jié)果值就是變量pet的動態(tài)值,而此結(jié)果值的類型*Dog就是該變量的動態(tài)類型。

動態(tài)類型這個叫法是相對于靜態(tài)類型而言的。對于變量pet來講,它的靜態(tài)類型就是Pet,并且永遠(yuǎn)是Pet,但是它的動態(tài)類型卻會隨著我們賦給它的動態(tài)值而變化。

比如,只有我把一個*Dog類型的值賦給變量pet之后,該變量的動態(tài)類型才會是*Dog。如果還有一個Pet接口的實現(xiàn)類型*Fish,并且我又把一個此類型的值賦給了pet,那么它的動態(tài)類型就會變?yōu)?Fish。

還有,在我們給一個接口類型的變量賦予實際的值之前,它的動態(tài)類型是不存在的

type Pet interface {SetName(name string)Name() stringCategory() string }type Dog struct {name string }func (dog *Dog) SetName(name string) {dog.name = name }func (dog *Dog) Name() string {return dog.name }func (dog *Dog) Category() string {return "dog" }func main() {// 示例1dog := Dog{"little pig"}_, ok := interface{}(dog).(Pet)fmt.Printf("Dog是接口Pet的實現(xiàn)類型嗎: %v\n", ok)_, ok = interface{}(&dog).(Pet)fmt.Printf("*Dog是接口Pet的實現(xiàn)類型嗎: %v\n", ok)fmt.Println()// 示例2var pet Pet = &dogfmt.Printf("This pet is a %s, the name is %q.\n", pet.Category(), pet.Name())}

實現(xiàn)規(guī)則

接口變量的值并不等同于這個可被稱為動態(tài)值的副本。它會包含兩個指針,一個指針指向動態(tài)值,一個指針指向類型信息

規(guī)則一:如果使用指針方法來實現(xiàn)一個接口,那么只有指向那個類型的指針才能夠?qū)崿F(xiàn)對應(yīng)的接口。

規(guī)則二:如果使用值方法來實現(xiàn)一個接口,那么那個類型的值和指針都能夠?qū)崿F(xiàn)對應(yīng)的接口

type Pet interface {SetName(name string)Name() stringCategory() string }type Dog struct {name string // 名字。 }func (dog *Dog) SetName(name string) {dog.name = name }func (dog Dog) Name() string {return dog.name }func (dog Dog) Category() string {return "dog" }func main() {// 示例1。dog := Dog{"little pig"}_, ok := interface{}(dog).(Pet)fmt.Printf("Dog implements interface Pet: %v\n", ok)_, ok = interface{}(&dog).(Pet)fmt.Printf("*Dog implements interface Pet: %v\n", ok)fmt.Println()// 示例2。var pet Pet = &dogfmt.Printf("This pet is a %s, the name is %q.\n",pet.Category(), pet.Name()) }

Stringer

在fmt包里有一個interface叫做Stringer:

type Stringer interface {String() string }

作用:fmt.Println或打印一個變量的值的時候,會判斷這個變量是否實現(xiàn)了Stringer接口,如果實現(xiàn)了,則調(diào)用這個變量的String()方法,并將返回值打印到屏幕上

fmt.Printf的%v也會讀取Stringer

例子:

package mainimport "fmt"type Person struct {Name stringAge int }func (p Person) String() string {return fmt.Sprintf("(Name: %v) (Age: %v)", p.Name, p.Age) }func main() {a := Person{"benz", 21}fmt.Println(a)fmt.Printf("%v\n", a) }

輸出

(Name: benz) (Age: 21) (Name: benz) (Age: 21)

可以看出,用String()修改了輸出格式
再舉個例子:

package mainimport "fmt"type IPAddr [4]byte/* func (ip IPAddr) String() string {return fmt.Sprintf("%v.%v.%v.%v", ip[0], ip[1], ip[2], ip[3]) } */func main() {hosts := map[string]IPAddr{"loopback": {127, 0, 0, 1},"googleDNS": {8, 8, 8, 8},}for name, ip := range hosts {fmt.Printf("%v: %v\n", name, ip)} }

輸出

loopback: [127 0 0 1] googleDNS: [8 8 8 8]

如果把上面的String的注釋去掉,輸出則是

loopback: 127.0.0.1 googleDNS: 8.8.8.8

type switch

對接口變量hold值做類型判斷?


當(dāng)i是個接口變量的時候,可以用i.(type)來對這個接口變量hold的值類型做判斷

switch v := i.(type) { case int:... case string:... default:... }

注意:之前類型斷言是用i.($TYPE)比如i.(int)來判斷是不是int類型,但是這里用關(guān)鍵字type。關(guān)鍵字type只能用在switch語句里,如果用在switch外面會報錯,比如:

a := i.(type) fmt.Printf("%v %T\n", a, a)

報錯:

use of .(type) outside type switch

除了能識別內(nèi)置類型,也可以識別其他類型,比如函數(shù)類型,或者帶指針的struct,比如這個例子是帶指針的struct

type Foo interface {foo() int }type MyStruct struct {X, Y int }func (a *MyStruct) foo() int {return a.X + a.Y }func main() {var f Foos := MyStruct{3, 4}f = &sfmt.Printf("%v,%T\n", f, f)switch v := f.(type) {case *MyStruct:fmt.Printf("1,%v,%T\n", v, v)default:fmt.Printf("2,%v,%T\n", v, v)} }

輸出

&{3 4},*main.MyStruct 1,&{3 4},*main.MyStruct

注意:這個type用%T顯示出來的是*main.MyStruct,而case里是*MyStruct,沒有main喔。

這個例子是函數(shù):

func main() {pos := func () int { return 1 }fmt.Printf("%v,%T\n", pos, pos)var i interface{}i = posfmt.Printf("%v,%T\n", i, i)switch v := i.(type) {case int:fmt.Printf("1,%v,%T\n", v, v)case func() int:fmt.Printf("2,%v,%T\n", v, v)case func(int) int:fmt.Printf("3,%v,%T\n", v, v)default:fmt.Printf("4,%v,%T\n", v, v)} }

輸出:

0x1088f30,func() int 0x1088f30,func() int 2,0x1088f30,func() int

可以看出:case后面跟的就是i值的類型

注意:case后面不能跟不存在的自定義類型,比如:

func main() {var i interface{}i = 1fmt.Printf("%v,%T\n", i, i)switch v := i.(type) {case int:fmt.Printf("1,%v,%T\n", v, v)case func() int:fmt.Printf("2,%v,%T\n", v, v)case func(int) int:fmt.Printf("3,%v,%T\n", v, v)case *MyStruct:fmt.Printf("4,%v,%T\n", v, v)default:fmt.Printf("5,%v,%T\n", v, v)} }

報錯:

undefined: MyStruct

可以用是否實現(xiàn)接口做判斷?


package mainimport "fmt"type Adder interface {Add() }type MyStruct struct {X, Y int }func (this MyStruct) Add() {fmt.Println(this.X + this.Y) }func main() {s := MyStruct{3, 4}//var i interface{} = svar i Adder = sswitch v := i.(type) {case MyStruct:fmt.Printf("case MyStruct: %T %v\n", v, v)case interface{}:fmt.Printf("case interface{}: %T %v\n", v, v)case Adder:fmt.Printf("case Adder: %T %v\n", v, v)default:fmt.Printf("not case: %T %v\n", v, v)} }

輸出

case MyStruct: main.MyStruct {3 4}

另外上面

  • var i interface{} = s或var i Adder = s用哪個都一樣

  • 這3個case不管用哪個,第一個case都能匹配到

  • Warning

    case只能用于類型判斷(包括實現(xiàn)接口),沒有辦法判斷是否為一個接口。就是說沒有辦法判斷一個變量是否為接口變量,即使用反射也是無法判斷的

    總結(jié)

    以上是生活随笔為你收集整理的golang中的接口的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。