Go語言接口與多態(tài)詳細(xì)介紹
接口與多態(tài)
1. 接口
1. 接口的定義
1、Go語言提供了接口數(shù)據(jù)類型。
2、接口就是把一些共性的方法集合在一起定義。
3、如果有實(shí)現(xiàn)類將接口定義的方法全部實(shí)現(xiàn)了,那么就代表實(shí)現(xiàn)了這個(gè)接口
4、隱式實(shí)現(xiàn) Go ,假設(shè)A實(shí)現(xiàn)了B接口中的所有方法,不需要顯示聲明
5、接口是方法的定義集合,不需要實(shí)現(xiàn)具體的方法內(nèi)容。名字約束
在Go語言中,接口(Interface)是一個(gè)重要的特性,它允許我們定義一組方法但不實(shí)現(xiàn)它們,任何類型只要實(shí)現(xiàn)了這些方法,就被認(rèn)為是實(shí)現(xiàn)了該接口。
接口體現(xiàn)了程序設(shè)計(jì)的多態(tài)、高內(nèi)聚、低耦合的思想,是實(shí)現(xiàn)面向?qū)ο缶幊讨卸鄳B(tài)性的關(guān)鍵工具。
接口通過interface關(guān)鍵字定義,它是一組方法的集合。接口中的方法沒有實(shí)現(xiàn)體,即它們沒有具體的實(shí)現(xiàn)代碼。一個(gè)類型只要實(shí)現(xiàn)了接口中的所有方法,就認(rèn)為該類型實(shí)現(xiàn)了該接口。
如果一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了這個(gè)接口所有的方法,那這個(gè)結(jié)構(gòu)體就是這個(gè)接口類型的
2. 接口應(yīng)用代碼示例
接口的基本語法如下:
type 接口名 interface { 方法名1(參數(shù)列表1) 返回值列表1 方法名2(參數(shù)列表2) 返回值列表2 ... }
package main import ( "fmt" ) // 接口: USB、typec、插座 // 1、Go語言提供了接口數(shù)據(jù)類型。 // 2、接口就是把一些共性的方法集合在一起定義。 // 3、如果有實(shí)現(xiàn)類將接口定義的方法全部實(shí)現(xiàn)了,那么就代表實(shí)現(xiàn)了這個(gè)接口 // 4、隱式實(shí)現(xiàn) Go ,假設(shè)A實(shí)現(xiàn)了B接口中的所有方法,不需要顯示聲明 // 5、接口是方法的定義集合,不需要實(shí)現(xiàn)具體的方法內(nèi)容。名字約束 // USB 接口的定義 interface 來定義,方法太多了,要?dú)w類,方法的集合 type USB interface { // 接口,方法的集合 input() // 輸入方法 output() // 輸出方法 } // Mouse 結(jié)構(gòu)體 type Mouse struct { name string } // 結(jié)構(gòu)體實(shí)現(xiàn)了接口的全部方法就代表實(shí)現(xiàn)了這個(gè)接口,否則不算實(shí)現(xiàn)這個(gè)接口 func (mouse Mouse) output() { fmt.Println(mouse.name, "鼠標(biāo)輸出") } func (mouse Mouse) input() { fmt.Println(mouse.name, "鼠標(biāo)輸入") } // 接口調(diào)用測(cè)試 func test(u USB) { u.input() u.output() } func main() { // 通過傳入接口實(shí)現(xiàn)類來進(jìn)行調(diào)用 m1 := Mouse{name: "羅技"} // test 參數(shù) USB 類型,如果一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了這個(gè)接口所有的方法,那這個(gè)結(jié)構(gòu)體就是這個(gè)接口類型的 test(m1) //也可以單獨(dú)測(cè)試接口 //m1.input() k1 := KeyBoard{name: "雷蛇"} test(k1) // 定義高級(jí)類型 k1就升級(jí)了 KeyBoard --> USB 向上轉(zhuǎn)型 var usb USB usb = k1 fmt.Println(usb) // 接口是無法使用實(shí)現(xiàn)類的屬性的 //fmt.Println(usb.name) } // KeyBoard 結(jié)構(gòu)體 type KeyBoard struct { name string } // 結(jié)構(gòu)體實(shí)現(xiàn)了接口的全部方法就代表實(shí)現(xiàn)了這個(gè)接口,否則不算實(shí)現(xiàn)這個(gè)接口 func (key KeyBoard) output() { fmt.Println(key.name, "鍵盤輸出") } func (key KeyBoard) input() { fmt.Println(key.name, "鍵盤輸入") }
帶有參數(shù)和返回值的接口
package main import "fmt" // Tongxin 定義接口 type Tongxin interface { //定義帶有參數(shù)和返回值的方法 dadianhua(youdian bool) string jieidanhua(youdian bool) string } // People 定義結(jié)構(gòu)體 type People struct { name string age int phone string } // 實(shí)現(xiàn)接口 func (p People) dadianhua(youdian bool) string { if youdian { return fmt.Sprintf("%v 打了電話", p.name) } else { return fmt.Sprintf("打電話時(shí)手機(jī)沒電了") } } func (p People) jieidanhua(youdian bool) string { if youdian { return fmt.Sprintf("%v 接了電話", p.name) } else { return fmt.Sprintf("接電話時(shí)手機(jī)沒電了") } } // 接口測(cè)試,有傳參,有返回值 func testdianhua(phone Tongxin) { str1 := phone.dadianhua(false) str2 := phone.jieidanhua(true) fmt.Println(str1, str2) } func main() { //創(chuàng)建對(duì)象 p := People{"jingtian", 18, "18898985898"} //如果一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)了這個(gè)接口所有的方法,那這個(gè)結(jié)構(gòu)體就是這個(gè)接口類型的 testdianhua(p) }
2. 模擬多態(tài)
多態(tài)是指相同的接口(方法)可以表現(xiàn)出不同的行為。在Go語言中,通過接口實(shí)現(xiàn)多態(tài)。
在Go語言中,接口定義了一組方法的集合,但不實(shí)現(xiàn)它們,而是由具體的類型來實(shí)現(xiàn)這些方法。
任何實(shí)現(xiàn)了接口中所有方法的類型都被視為該接口的實(shí)現(xiàn)。接口是Go語言中實(shí)現(xiàn)多態(tài)性的關(guān)鍵。
多態(tài):一個(gè)事務(wù)有多種形態(tài)
父類:動(dòng)物
子類:貓
子類:狗
貓和狗是多態(tài)的,他們既可以是自己,也可以是動(dòng)物,這個(gè)就是多態(tài),一個(gè)事務(wù)有多種形態(tài)
Go語言中多態(tài)的實(shí)現(xiàn)
定義接口
首先,我們需要定義一個(gè)接口,該接口包含了一組需要被實(shí)現(xiàn)的方法。例如,我們可以定義一個(gè)Shape接口,用于計(jì)算不同形狀的面積。
type Shape interface { Area() float64 }
在這個(gè)接口中,我們定義了一個(gè)Area()方法,該方法返回一個(gè)float64類型的值,表示形狀的面積。
實(shí)現(xiàn)接口
接下來,我們需要定義具體的類型來實(shí)現(xiàn)這個(gè)接口。這些類型將提供Area()方法的具體實(shí)現(xiàn)。
矩形
type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height }
圓形
type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius }
使用接口進(jìn)行多態(tài)調(diào)用
現(xiàn)在,我們可以使用Shape接口來創(chuàng)建不同類型的形狀對(duì)象,并通過接口進(jìn)行多態(tài)調(diào)用。
func main() { r := Rectangle{Width: 4, Height: 5} c := Circle{Radius: 3} shapes := []Shape{r, c} for _, shape := range shapes { fmt.Printf("Area: %f\n", shape.Area()) } }
完整代碼
package main import ( "fmt" "math" ) type Shape interface { Area() float64 } // Rectangle 矩形 type Rectangle struct { Width float64 Height float64 } func (r Rectangle) Area() float64 { return r.Width * r.Height } // Circle 圓形 type Circle struct { Radius float64 } func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius } func main() { r := Rectangle{Width: 4, Height: 5} c := Circle{Radius: 3} shapes := []Shape{r, c} for _, shape := range shapes { fmt.Printf("Area: %f\n", shape.Area()) } }
在上面的代碼中,我們創(chuàng)建了一個(gè)shapes切片,該切片包含了不同類型的形狀對(duì)象(矩形和圓形)。
然后,我們遍歷shapes切片,并通過Shape接口調(diào)用Area()方法。由于這兩種形狀都實(shí)現(xiàn)了Shape接口,因此多態(tài)性使我們能夠以一致的方式調(diào)用它們的Area()方法。
多態(tài)案例2:
package main import "fmt" // Animal3 定義接口 type Animal3 interface { eat() sleep() } type Dog3 struct { name string } func (dog Dog3) eat() { fmt.Println(dog.name, "--eat") } func (dog Dog3) sleep() { fmt.Println(dog.name, "--sleep") } // 多態(tài) func main() { // Dog 兩重身份:1、Dog 2、Animal ,多態(tài) dog1 := Dog3{name: "旺財(cái)"} dog1.eat() dog1.sleep() // Dog 也可以是 Animal test2(dog1) // 定義一個(gè)類型可以為接口類型的變量 // 實(shí)際上所有實(shí)現(xiàn)類都可以賦值給這個(gè)對(duì)象 var animal Animal3 // 模糊的 -- 具體化,將具體的實(shí)現(xiàn)類賦值給他,才有意義 animal = dog1 //接口是無法使用實(shí)現(xiàn)類的屬性的 test2(animal) } // Animal 接口 func test2(a Animal3) { a.eat() a.sleep() }
接口的實(shí)現(xiàn)類都擁有多態(tài)特性:除了自己本身還是他對(duì)應(yīng)接口的類型。
3. 空接口
空接口interface{}不包含任何方法,因此任何類型都實(shí)現(xiàn)了空接口??战涌诳梢员灰暈槟苎b入任意數(shù)量、任意數(shù)據(jù)類型的數(shù)據(jù)容器。
因此空接口可以存儲(chǔ)任何的類型
空接口不好記,因此在新版本go中起了個(gè)名字,叫any
interface{} == any
之所以我們的fmt.Println能打印所有東西,就是因?yàn)樗鼈魅氲膮?shù)就是any,而any的類型就是空接口
點(diǎn)擊any進(jìn)去看看,就是空接口
package main import "fmt" // A 定義空接口 type A interface{} // Dogg 所有結(jié)構(gòu)體都實(shí)現(xiàn)了空接口A type Dogg struct { name string } type Catt struct { name string } func testNow(a A) { fmt.Println(a) } // 可以指定定義空接口 // // any is an alias for interface{} and is equivalent to interface{} in all ways. // type any = interface{} // 可以傳入任何東西 func testNow2(temp interface{}) { } func main() { //A類型可以是任何類型 var a1 A = Catt{name: "喵喵"} var a2 A = Dogg{name: "旺財(cái)"} var a3 A = 1 var a4 A = "景天科技苑" fmt.Println(a1) fmt.Println(a2) fmt.Println(a3) fmt.Println(a4) testNow(a1) // map結(jié)合空接口,就可以存儲(chǔ)任何類型數(shù)據(jù) map1 := make(map[string]interface{}) map1["name"] = "dajiang" map1["age"] = 18 fmt.Println(map1) // slice,切片定義成空接口類型,也可以存放任何類型數(shù)據(jù) s1 := make([]any, 0, 10) s1 = append(s1, 1, "12312", false, a1, a2) fmt.Println(s1) //數(shù)組空接口,數(shù)組里面的值默認(rèn)是nil,也可以存放任何數(shù)據(jù)類型 var arr [4]interface{} fmt.Println(arr) arr[0] = 3 arr[1] = "2" arr[2] = s1 arr[3] = true fmt.Println(arr) }
4. 接口嵌套
接口可以嵌套其他接口,即一個(gè)接口可以繼承多個(gè)別的接口。這時(shí),如果要實(shí)現(xiàn)這個(gè)接口,必須實(shí)現(xiàn)它繼承的所有接口的方法。
package main import ( "fmt" ) type AA interface { test1() } type BB interface { test2() } // CC 接口嵌套 CC : test1()/test2()/test3() // 如果要實(shí)現(xiàn)接口CC,那么需要實(shí)現(xiàn)這個(gè)三個(gè)方法。那這個(gè)對(duì)象就有3個(gè)接口可以轉(zhuǎn)型。 type CC interface { AA // 導(dǎo)入AA接口中的方法 BB test3() } // Dog7 編寫一個(gè)結(jié)構(gòu)體實(shí)現(xiàn)接口CC type Dog7 struct { } func (dog Dog7) test1() { fmt.Println("test1") } func (dog Dog7) test2() { fmt.Println("test2") } func (dog Dog7) test3() { fmt.Println("test3") } func main() { // dog 擁有4種形態(tài): Dog7 、CC 、 BB 、 AA var dog Dog7 = Dog7{} dog.test1() dog.test2() dog.test3() // 接口對(duì)象只能調(diào)用自己接口里面的方法 var a AA = dog a.test1() //a.test2() // 向上轉(zhuǎn)型之后只能調(diào)用它自己對(duì)應(yīng)的方法 var b BB = dog b.test2() //c三個(gè)方法都可以調(diào)用 var c CC = dog c.test1() c.test2() c.test3() }
5. 接口斷言
接口斷言用于檢查接口變量是否持有特定類型的值,并獲取該值。被斷言的對(duì)象必須是接口類型,否則會(huì)報(bào)錯(cuò)
它有兩種形式:不安全斷言和類型安全的斷言。
不安全斷言
instance := 接口對(duì)象.(實(shí)際類型)
如果不滿足類型斷言,程序?qū)l(fā)生panic報(bào)錯(cuò)。
package main import "fmt" // 斷言 t := i.(T) t:t就是i接口是T類型的 i:接口 T:類型 // 語法:t,ok:= i.(T) ok 隱藏返回值,如果斷言成功 ok就是true、否則就是false func main() { //assertsString("11111111111") assertsString(true) // panic: interface conversion: interface {} is bool, not string } // 判斷一個(gè)變量是不是string類型的 func assertsString(i interface{}) { // 如果斷言失敗,則會(huì)拋出 panic 恐慌,程序就會(huì)停止執(zhí)行。 s := i.(string) fmt.Println(s) }
類型安全的斷言
instance, ok := 接口對(duì)象.(實(shí)際類型)
語法:t,ok:= i.(T) ok 隱藏返回值,如果斷言成功 ok就是true、否則就是false
如果斷言失敗,ok將會(huì)是false,而instance將會(huì)是類型的零值,并且不會(huì)觸發(fā)panic。
接口斷言代碼示例
package main import "fmt" // 斷言 t := i.(T) t:t就是i接口是T類型的 i:接口 T:類型 // 語法:t,ok:= i.(T) ok 隱藏返回值,如果斷言成功 ok就是true、否則就是false func main() { //assertsString("11111111111") assertsInt("中國") } // 斷言失敗的情況,我們希望程序不會(huì)停止。 func assertsInt(i any) { r, ok := i.(int) if ok { fmt.Println("是我們期望的結(jié)果 int") fmt.Println(r) } else { fmt.Println("不是我們期望的結(jié)果,無法執(zhí)行預(yù)期操作") } }
多個(gè)預(yù)期結(jié)果判斷
通過switch來判斷 switch i.(T)
i 必須是接口類型
i.(type)必須在switch中使用
package main import "fmt" // 通過switch來判斷 switch i.(T) type I interface{} // 如果斷言的類型同時(shí)實(shí)現(xiàn)了switch 多個(gè)case匹配,默認(rèn)使用第一個(gè)case // 所以要把范圍更小的匹配放前面 func testAssert(i interface{}) { // switch i.(type) 接口斷言 //i.(type)必須在switch中使用 switch i.(type) { case string: fmt.Println("變量為string類型") case int: fmt.Println("變量為int類型") case nil: fmt.Println("變量為nil類型") case map[string]int: fmt.Println("map類型") case interface{}: fmt.Println("變量為interface{}類型") //空接口與I一樣 case I: fmt.Println("變量為I類型") // ..... default: fmt.Println("未知類型") } } func main() { testAssert("string") testAssert(1) var i I // 沒有初始化空接口時(shí),默認(rèn)值為 nil類型 不屬于I類型 var i2 I = 1 // 只有賦值了之后,才是對(duì)應(yīng)的類型 testAssert(i) testAssert(i2) //map類型 j := make(map[string]int) testAssert(j) }
到此這篇關(guān)于Go語言接口與多態(tài)的文章就介紹到這了,更多相關(guān)Go語言接口與多態(tài)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang Gin框架實(shí)現(xiàn)文件下載功能的示例代碼
本文主要介紹了Golang Gin框架實(shí)現(xiàn)文件下載功能的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12GO語言導(dǎo)入自己寫的包(同級(jí)目錄和不同目錄)
本文介紹了如何在Go語言項(xiàng)目中導(dǎo)入同級(jí)目錄和不同目錄的包,詳細(xì)解釋了創(chuàng)建文件結(jié)構(gòu)、編寫主函數(shù)、同級(jí)目錄和不同目錄方法的調(diào)用,適合初學(xué)者參考,幫助理解Go項(xiàng)目的基本構(gòu)建和包管理2024-09-09Golang實(shí)現(xiàn)請(qǐng)求限流的幾種辦法(小結(jié))
這篇文章主要介紹了Golang實(shí)現(xiàn)請(qǐng)求限流的幾種辦法(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10Golang中常見的三種并發(fā)控制方式使用小結(jié)
這篇文章主要為大家詳細(xì)介紹了如何對(duì)goroutine并發(fā)行為的控制,在Go中最常見的有三種方式:sync.WaitGroup、channel和Context,下面我們就來看看他們的具體使用吧2024-01-01