從零開始學(xué)Golang的接口
前言
接口在面向?qū)ο缶幊讨惺墙?jīng)常使用的招式,也是體現(xiàn)多態(tài)很重要的手段。
是的。Golang中也有接口這玩意兒。
1.為什么需要接口?
多數(shù)情況下,數(shù)據(jù)可能包含不同的類型,卻會有一個(gè)或者多個(gè)共同點(diǎn),這些共同點(diǎn)就是抽象的基礎(chǔ)。前文講到的Golang繼承解決的是is-a的問題,單一繼承的關(guān)系。但是當(dāng)不同的父類具有相同的行為的時(shí)候,單一繼承就沒法解決了。
于是乎,接口出現(xiàn)了。接口可以理解為某一個(gè)方面的抽象,可以是多對一的(多個(gè)類型實(shí)現(xiàn)一個(gè)接口),這也是多態(tài)的體現(xiàn)。解決了上文一對一的問題。
2.接口是什么?如何定義?
是什么
接口是一組僅包含方法名、參數(shù)、返回值的未具體實(shí)現(xiàn)的方法的集合。
如果實(shí)現(xiàn)了接口的所有方法,則認(rèn)為實(shí)現(xiàn)了該接口,無需在該類型上顯示的添加聲明。
這個(gè)解釋下,加深印象,在php中接口是長這樣的:
//定義接口
interface base{
? ?public function getName();
}
?
//學(xué)生類
class student implements base{
? ?public function getName(){
? ? ? echo "咖啡色的羊駝";
? ?}
}這里有個(gè)關(guān)鍵字:implements。
這樣的聲明稱之為顯示的,而在Golang中接口是隱式地實(shí)現(xiàn)。(埋個(gè)伏筆看下文)
定義
type interfaceName interface {?
?? ?// 方法列表?
? ? GetName() string
}?3.接口實(shí)戰(zhàn)初體驗(yàn)
實(shí)際編程中呢,接口的命名大伙兒喜歡使用er結(jié)尾。當(dāng)然這個(gè)看個(gè)人喜好。
上代碼:
?? ?package main
?? ?import (
?? ??? ?"fmt"
?? ?)
?? ?
?? ?// 定義一個(gè)接口
?? ?type People interface {
?? ??? ?ReturnName() string
?? ?}
?? ?
?? ?// 定義一個(gè)結(jié)構(gòu)體
?? ?type Student struct {
?? ??? ?Name string
?? ?}
?? ?
?? ?// 定義結(jié)構(gòu)體的一個(gè)方法。
?? ?// 突然發(fā)現(xiàn)這個(gè)方法同接口People的所有方法(就一個(gè)),此時(shí)可直接認(rèn)為結(jié)構(gòu)體Student實(shí)現(xiàn)了接口People
?? ?func (s Student) ReturnName() string {
?? ??? ?return s.Name
?? ?}
?? ?
?? ?func main() {
?? ??? ?cbs := Student{Name:"咖啡色的羊駝"}
?? ?
?? ??? ?var a People
?? ??? ?// 因?yàn)镾tudents實(shí)現(xiàn)了接口所以直接賦值沒問題
?? ??? ?// 如果沒實(shí)現(xiàn)會報(bào)錯: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.如何測試是否已實(shí)現(xiàn)該接口?
使用接口特有的斷言判斷來實(shí)現(xiàn)(下文還會再次提到,加深印象)。
語法:x.(T)
這樣的語法只適應(yīng)于x是interface類型
接著上文例子,繼續(xù)上代碼:
?? ?// 由于x.(T)只能是接口類型判斷,所以傳參時(shí)候,傳入的是接口類型
?? ?// 為何test的類型可以是一個(gè)空接口?埋伏筆下文便知。
?? ?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.空接口&類型斷言
空接口
空接口就是不包含任何方法的接口。正因?yàn)槿绱?,所有的類型都?shí)現(xiàn)了空接口。
雖然空接口起不到任何作用,但是空接口在需要存儲任何類型數(shù)值的時(shí)候非常有用,這也回答了上文的問題,因?yàn)榭战涌诳梢源鎯θ我忸愋偷臄?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"
?? ?)
?? ?
?? ?// 定義一個(gè)結(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.一個(gè)類型實(shí)現(xiàn)多個(gè)接口
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 "學(xué)生"
}
func main() {
cbs := Student{Name: "咖啡色的羊駝"}
var a People // 定義a為People接口類型
var b Role // 定義b為Role接口類型
a = cbs // 由于Student實(shí)現(xiàn)了People所有方法,所以接口實(shí)現(xiàn)成功,可直接賦值
b = cbs // 由于Student實(shí)現(xiàn)了Role所有方法,所以接口實(shí)現(xiàn)成功,可直接賦值
name := a.ReturnName()
fmt.Println(name) // 輸出"咖啡色的羊駝"
role := b.ReturnRole()
fmt.Println(role) // 輸出"學(xué)生"
}也說明一個(gè)東西:實(shí)現(xiàn)了某個(gè)接口的類型,還可以有其它的方法。只要是方法實(shí)現(xiàn)包含接口的即可。
8.指針與值類型實(shí)現(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 // 由于是指針類型,所以賦值的時(shí)候需要加上&
name = a.ReturnName()
fmt.Println(name) // 輸出"咖啡色的羊駝的老師"
}
"a = sss"這樣寫會發(fā)生報(bào)錯:
cannot use sss (type Teacher) as type People in assignment:
Teacher does not implement People (ReturnName method has pointer receiver)
因?yàn)槭荰eacher的指針實(shí)現(xiàn)了ReturnName方法,Teacher本身沒實(shí)現(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 "學(xué)生"
}
func main() {
cbs := Student{Name: "咖啡色的羊駝"}
var a Role
a = cbs
name := a.ReturnName()
fmt.Println(name)
role := a.ReturnRole()
fmt.Println(role)
}
到此這篇關(guān)于從零開始學(xué)Golang的接口的文章就介紹到這了,更多相關(guān)Golang 接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go-micro集成RabbitMQ實(shí)戰(zhàn)和原理詳解
本文主要介紹go-micro使用RabbitMQ收發(fā)數(shù)據(jù)的方法和原理,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Go?1.21.0?新增結(jié)構(gòu)化日志記錄標(biāo)準(zhǔn)庫log/slog使用詳解
這篇文章主要為大家介紹了Go?1.21.0?新增結(jié)構(gòu)化日志記錄標(biāo)準(zhǔn)庫log/slog使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Go語言bufio庫的全面指南與實(shí)戰(zhàn)技巧詳解
這篇文章主要為大家全面介紹一下?bufio?庫的核心組件與功能,包括?Reader、Writer?和?Scanner?等并深入探討它們在實(shí)際編程中的運(yùn)用場景和技巧,感興趣的可以了解下2024-01-01
Golang標(biāo)準(zhǔn)庫unsafe源碼解讀
這篇文章主要為大家介紹了Golang標(biāo)準(zhǔn)庫unsafe源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
Golang實(shí)現(xiàn)CronJob(定時(shí)任務(wù))的方法詳解
這篇文章主要為大家詳細(xì)介紹了Golang如何通過一個(gè)單 pod 去實(shí)現(xiàn)一個(gè)常駐服務(wù),去跑定時(shí)任務(wù)(CronJob),文中的示例代碼講解詳細(xì),需要的可以參考下2023-04-04
golang 實(shí)現(xiàn)比特幣內(nèi)核之處理橢圓曲線中的天文數(shù)字
比特幣密碼學(xué)中涉及到的大數(shù)運(yùn)算超出常規(guī)整數(shù)范圍,需使用golang的big包進(jìn)行處理,通過使用big.Int類型,能有效避免整數(shù)溢出,并保持邏輯正確性,測試展示了在不同質(zhì)數(shù)模下的運(yùn)算結(jié)果,驗(yàn)證了邏輯的準(zhǔn)確性,此外,探討了費(fèi)馬小定理在有限字段除法運(yùn)算中的應(yīng)用2024-11-11
Go語言學(xué)習(xí)之結(jié)構(gòu)體和方法使用詳解
這篇文章主要為大家詳細(xì)介紹了Go語言中結(jié)構(gòu)體和方法的使用,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Go語言有一定的幫助,需要的可以參考一下2022-04-04
Go語言實(shí)現(xiàn)的簡單網(wǎng)絡(luò)端口掃描方法
這篇文章主要介紹了Go語言實(shí)現(xiàn)的簡單網(wǎng)絡(luò)端口掃描方法,實(shí)例分析了Go語言網(wǎng)絡(luò)程序的實(shí)現(xiàn)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02

