從零開始學Golang的接口
前言
接口在面向?qū)ο缶幊讨惺墙?jīng)常使用的招式,也是體現(xiàn)多態(tài)很重要的手段。
是的。Golang中也有接口這玩意兒。
1.為什么需要接口?
多數(shù)情況下,數(shù)據(jù)可能包含不同的類型,卻會有一個或者多個共同點,這些共同點就是抽象的基礎(chǔ)。前文講到的Golang繼承解決的是is-a的問題,單一繼承的關(guān)系。但是當不同的父類具有相同的行為的時候,單一繼承就沒法解決了。
于是乎,接口出現(xiàn)了。接口可以理解為某一個方面的抽象,可以是多對一的(多個類型實現(xiàn)一個接口),這也是多態(tài)的體現(xiàn)。解決了上文一對一的問題。
2.接口是什么?如何定義?
是什么
接口是一組僅包含方法名、參數(shù)、返回值的未具體實現(xiàn)的方法的集合。
如果實現(xiàn)了接口的所有方法,則認為實現(xiàn)了該接口,無需在該類型上顯示的添加聲明。
這個解釋下,加深印象,在php中接口是長這樣的:
//定義接口 interface base{ ? ?public function getName(); } ? //學生類 class student implements base{ ? ?public function getName(){ ? ? ? echo "咖啡色的羊駝"; ? ?} }
這里有個關(guān)鍵字:implements。
這樣的聲明稱之為顯示的,而在Golang中接口是隱式地實現(xiàn)。(埋個伏筆看下文)
定義
type interfaceName interface {? ?? ?// 方法列表? ? ? GetName() string }?
3.接口實戰(zhàn)初體驗
實際編程中呢,接口的命名大伙兒喜歡使用er結(jié)尾。當然這個看個人喜好。
上代碼:
?? ?package main ?? ?import ( ?? ??? ?"fmt" ?? ?) ?? ? ?? ?// 定義一個接口 ?? ?type People interface { ?? ??? ?ReturnName() string ?? ?} ?? ? ?? ?// 定義一個結(jié)構(gòu)體 ?? ?type Student struct { ?? ??? ?Name string ?? ?} ?? ? ?? ?// 定義結(jié)構(gòu)體的一個方法。 ?? ?// 突然發(fā)現(xiàn)這個方法同接口People的所有方法(就一個),此時可直接認為結(jié)構(gòu)體Student實現(xiàn)了接口People ?? ?func (s Student) ReturnName() string { ?? ??? ?return s.Name ?? ?} ?? ? ?? ?func main() { ?? ??? ?cbs := Student{Name:"咖啡色的羊駝"} ?? ? ?? ??? ?var a People ?? ??? ?// 因為Students實現(xiàn)了接口所以直接賦值沒問題 ?? ??? ?// 如果沒實現(xiàn)會報錯:cannot use cbs (type Student) as type People in assignment:Student does not implement People (missing ReturnName method) ?? ??? ?a = cbs ? ? ?? ?? ??? ?name := a.ReturnName()? ?? ??? ?fmt.Println(name) // 輸出"咖啡色的羊駝" ?? ?}
4.如何測試是否已實現(xiàn)該接口?
使用接口特有的斷言判斷來實現(xiàn)(下文還會再次提到,加深印象)。
語法:x.(T)
這樣的語法只適應(yīng)于x是interface類型
接著上文例子,繼續(xù)上代碼:
?? ?// 由于x.(T)只能是接口類型判斷,所以傳參時候,傳入的是接口類型 ?? ?// 為何test的類型可以是一個空接口?埋伏筆下文便知。 ?? ?func CheckPeople(test interface{}) { ?? ??? ?if _, ok := test.(People); ok { ?? ? ? ??? ?fmt.Printf("Student implements People") ?? ??? ?} ?? ?} ?? ? ?? ?func main() { ?? ??? ?cbs := Student{Name:"咖啡色的羊駝"} ?? ??? ?CheckPeople(cbs) // Student implements People ?? ?}
5.空接口&類型斷言
空接口
空接口就是不包含任何方法的接口。正因為如此,所有的類型都實現(xiàn)了空接口。
雖然空接口起不到任何作用,但是空接口在需要存儲任何類型數(shù)值的時候非常有用,這也回答了上文的問題,因為空接口可以存儲任意類型的數(shù)據(jù)。
?? ?// 定義cbs為空接口 ? ? var cbs interface{} ? ? var i int = 5 ? ? var s string = "Hello world" ? ? // cbs可以存儲任意類型的數(shù)值 ? ? cbs = i ? ? cbs = s
類型斷言
既然空接口可以存儲任意類型,那么如何區(qū)分不同的類型?
常用的有兩種方法:Comma-ok斷言、switch判斷。
上代碼:
?? ?package main ?? ? ?? ?import ( ?? ??? ?"fmt" ?? ?) ?? ? ?? ?// 定義一個結(jié)構(gòu)體 ?? ?type Student struct { ?? ??? ?Name string ?? ?} ?? ? ?? ?// 類型斷言 ?? ?func main() { ?? ? ? ?Params := make([]interface{}, 3) ?? ??? ?Params[0] = 88 ? ? ? ? ? ? ? ? ? // 整型 ?? ??? ?Params[1] = "咖啡色的羊駝" ? ? ? ? // 字符串 ?? ??? ?Params[2] = Student{Name: "cbs"} // 自定義結(jié)構(gòu)體類型 ?? ??? ? ?? ??? ?// Comma-ok斷言 ?? ??? ?for index, v := range Params { ?? ??? ??? ?if _, ok := v.(int); ok { ?? ??? ??? ??? ?fmt.Printf("Params[%d] 是int類型 \n", index) ?? ??? ??? ?} else if _, ok := v.(string); ok { ?? ??? ??? ??? ?fmt.Printf("Params[%d] 是字符串類型\n", index) ?? ??? ??? ?} else if _, ok := v.(Student); ok { ?? ??? ??? ??? ?fmt.Printf("Params[%d] 是自定義結(jié)構(gòu)體類型\n", index) ?? ??? ??? ?} else { ?? ??? ??? ??? ?fmt.Printf("list[%d] 未知類型\n", index) ?? ??? ??? ?} ?? ??? ?} ?? ??? ? ?? ??? ?// switch判斷 ?? ??? ?for index, v := range Params { ?? ??? ??? ?switch ?value := v.(type) { ?? ? ? ? ? ?case int: ?? ? ? ? ? ? ? ?fmt.Printf("Params[%d] 是int類型, 值:%d \n", index,value) ?? ? ? ? ? ?case string: ?? ? ? ? ? ? ? ?fmt.Printf("Params[%d] 是字符串類型, 值:%s\n", index,value) ?? ? ? ? ? ?case Student: ?? ? ? ? ? ? ? ?fmt.Printf("Params[%d] 是Person類型, 值:%s\n", index,value) ?? ? ? ? ? ?default: ?? ? ? ? ? ? ? ?fmt.Printf("list[%d] 未知類型\n", index) ?? ? ? ? ? ?}? ?? ??? ? ?? ??? ?} ? ?? ?}
6.接口零值
接口的零值是nil
package main import ( "fmt" ) type People interface { GetName() string } // 輸出 "cbs is nil 類型" func main() { var cbs People if cbs == nil { fmt.Println("cbs is nil 類型") } }
7.一個類型實現(xiàn)多個接口
package main import ( "fmt" ) type People interface { ReturnName() string } type Role interface { ReturnRole() string } type Student struct { Name string } func (s Student) ReturnName() string { return s.Name } func (s Student) ReturnRole() string { return "學生" } func main() { cbs := Student{Name: "咖啡色的羊駝"} var a People // 定義a為People接口類型 var b Role // 定義b為Role接口類型 a = cbs // 由于Student實現(xiàn)了People所有方法,所以接口實現(xiàn)成功,可直接賦值 b = cbs // 由于Student實現(xiàn)了Role所有方法,所以接口實現(xiàn)成功,可直接賦值 name := a.ReturnName() fmt.Println(name) // 輸出"咖啡色的羊駝" role := b.ReturnRole() fmt.Println(role) // 輸出"學生" }
也說明一個東西:實現(xiàn)了某個接口的類型,還可以有其它的方法。只要是方法實現(xiàn)包含接口的即可。
8.指針與值類型實現(xiàn)接口的區(qū)別
package main import ( "fmt" ) type People interface { ReturnName() string } type Student struct { Name string } type Teacher struct { Name string } func (s Student) ReturnName() string { return s.Name } func (t *Teacher) ReturnName() string { return t.Name } func main() { cbs := Student{Name: "咖啡色的羊駝"} sss := Teacher{Name: "咖啡色的羊駝的老師"} // 值類型 var a People a = cbs name := a.ReturnName() fmt.Println(name) // 指針類型 // a = sss <- 這樣寫不行?。?! a = &sss // 由于是指針類型,所以賦值的時候需要加上& name = a.ReturnName() fmt.Println(name) // 輸出"咖啡色的羊駝的老師" }
"a = sss"這樣寫會發(fā)生報錯:
cannot use sss (type Teacher) as type People in assignment:
Teacher does not implement People (ReturnName method has pointer receiver)
因為是Teacher的指針實現(xiàn)了ReturnName方法,Teacher本身沒實現(xiàn)。
9.接口嵌套
類似于PHP的接口繼承,Golang也有它的接口嵌套。
package main import ( "fmt" ) type People interface { ReturnName() string } type Role interface { People // 接口嵌套 ReturnRole() string } type Student struct { Name string } func (s Student) ReturnName() string { return s.Name } func (s Student) ReturnRole() string { return "學生" } func main() { cbs := Student{Name: "咖啡色的羊駝"} var a Role a = cbs name := a.ReturnName() fmt.Println(name) role := a.ReturnRole() fmt.Println(role) }
到此這篇關(guān)于從零開始學Golang的接口的文章就介紹到這了,更多相關(guān)Golang 接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang創(chuàng)建文件目錄os.Mkdir,os.MkdirAll的區(qū)別說明
本文主要講述os.Mkdir、os.MkdirAll區(qū)別以及在創(chuàng)建文件目錄過程中的一些其他技巧,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03Ruby序列化和持久化存儲(Marshal、Pstore)操作方法詳解
這篇文章主要介紹了Ruby序列化和持久化存儲(Marshal、Pstore)操作方法詳解,包括Ruby Marshal序列化,Ruby Pstore存儲,需要的朋友可以參考下2022-04-04