Go語言接口與多態(tài)詳細介紹

接口與多態(tài)
1. 接口
1. 接口的定義
1、Go語言提供了接口數(shù)據(jù)類型。
2、接口就是把一些共性的方法集合在一起定義。
3、如果有實現(xiàn)類將接口定義的方法全部實現(xiàn)了,那么就代表實現(xiàn)了這個接口
4、隱式實現(xiàn) Go ,假設(shè)A實現(xiàn)了B接口中的所有方法,不需要顯示聲明
5、接口是方法的定義集合,不需要實現(xiàn)具體的方法內(nèi)容。名字約束
在Go語言中,接口(Interface)是一個重要的特性,它允許我們定義一組方法但不實現(xiàn)它們,任何類型只要實現(xiàn)了這些方法,就被認為是實現(xiàn)了該接口。
接口體現(xiàn)了程序設(shè)計的多態(tài)、高內(nèi)聚、低耦合的思想,是實現(xiàn)面向?qū)ο缶幊讨卸鄳B(tài)性的關(guān)鍵工具。
接口通過interface關(guān)鍵字定義,它是一組方法的集合。接口中的方法沒有實現(xiàn)體,即它們沒有具體的實現(xiàn)代碼。一個類型只要實現(xiàn)了接口中的所有方法,就認為該類型實現(xiàn)了該接口。
如果一個結(jié)構(gòu)體實現(xiàn)了這個接口所有的方法,那這個結(jié)構(gòu)體就是這個接口類型的
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、如果有實現(xiàn)類將接口定義的方法全部實現(xiàn)了,那么就代表實現(xiàn)了這個接口
// 4、隱式實現(xiàn) Go ,假設(shè)A實現(xiàn)了B接口中的所有方法,不需要顯示聲明
// 5、接口是方法的定義集合,不需要實現(xiàn)具體的方法內(nèi)容。名字約束
// USB 接口的定義 interface 來定義,方法太多了,要歸類,方法的集合
type USB interface { // 接口,方法的集合
input() // 輸入方法
output() // 輸出方法
}
// Mouse 結(jié)構(gòu)體
type Mouse struct {
name string
}
// 結(jié)構(gòu)體實現(xiàn)了接口的全部方法就代表實現(xiàn)了這個接口,否則不算實現(xiàn)這個接口
func (mouse Mouse) output() {
fmt.Println(mouse.name, "鼠標輸出")
}
func (mouse Mouse) input() {
fmt.Println(mouse.name, "鼠標輸入")
}
// 接口調(diào)用測試
func test(u USB) {
u.input()
u.output()
}
func main() {
// 通過傳入接口實現(xiàn)類來進行調(diào)用
m1 := Mouse{name: "羅技"}
// test 參數(shù) USB 類型,如果一個結(jié)構(gòu)體實現(xiàn)了這個接口所有的方法,那這個結(jié)構(gòu)體就是這個接口類型的
test(m1)
//也可以單獨測試接口
//m1.input()
k1 := KeyBoard{name: "雷蛇"}
test(k1)
// 定義高級類型 k1就升級了 KeyBoard --> USB 向上轉(zhuǎn)型
var usb USB
usb = k1
fmt.Println(usb)
// 接口是無法使用實現(xiàn)類的屬性的
//fmt.Println(usb.name)
}
// KeyBoard 結(jié)構(gòu)體
type KeyBoard struct {
name string
}
// 結(jié)構(gòu)體實現(xiàn)了接口的全部方法就代表實現(xiàn)了這個接口,否則不算實現(xiàn)這個接口
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
}
// 實現(xiàn)接口
func (p People) dadianhua(youdian bool) string {
if youdian {
return fmt.Sprintf("%v 打了電話", p.name)
} else {
return fmt.Sprintf("打電話時手機沒電了")
}
}
func (p People) jieidanhua(youdian bool) string {
if youdian {
return fmt.Sprintf("%v 接了電話", p.name)
} else {
return fmt.Sprintf("接電話時手機沒電了")
}
}
// 接口測試,有傳參,有返回值
func testdianhua(phone Tongxin) {
str1 := phone.dadianhua(false)
str2 := phone.jieidanhua(true)
fmt.Println(str1, str2)
}
func main() {
//創(chuàng)建對象
p := People{"jingtian", 18, "18898985898"}
//如果一個結(jié)構(gòu)體實現(xiàn)了這個接口所有的方法,那這個結(jié)構(gòu)體就是這個接口類型的
testdianhua(p)
}
2. 模擬多態(tài)
多態(tài)是指相同的接口(方法)可以表現(xiàn)出不同的行為。在Go語言中,通過接口實現(xiàn)多態(tài)。
在Go語言中,接口定義了一組方法的集合,但不實現(xiàn)它們,而是由具體的類型來實現(xiàn)這些方法。
任何實現(xiàn)了接口中所有方法的類型都被視為該接口的實現(xiàn)。接口是Go語言中實現(xiàn)多態(tài)性的關(guān)鍵。
多態(tài):一個事務(wù)有多種形態(tài)
父類:動物
子類:貓
子類:狗
貓和狗是多態(tài)的,他們既可以是自己,也可以是動物,這個就是多態(tài),一個事務(wù)有多種形態(tài)
Go語言中多態(tài)的實現(xiàn)
定義接口
首先,我們需要定義一個接口,該接口包含了一組需要被實現(xiàn)的方法。例如,我們可以定義一個Shape接口,用于計算不同形狀的面積。
type Shape interface {
Area() float64
}在這個接口中,我們定義了一個Area()方法,該方法返回一個float64類型的值,表示形狀的面積。
實現(xiàn)接口
接下來,我們需要定義具體的類型來實現(xiàn)這個接口。這些類型將提供Area()方法的具體實現(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
}使用接口進行多態(tài)調(diào)用
現(xiàn)在,我們可以使用Shape接口來創(chuàng)建不同類型的形狀對象,并通過接口進行多態(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)建了一個shapes切片,該切片包含了不同類型的形狀對象(矩形和圓形)。
然后,我們遍歷shapes切片,并通過Shape接口調(diào)用Area()方法。由于這兩種形狀都實現(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: "旺財"}
dog1.eat()
dog1.sleep()
// Dog 也可以是 Animal
test2(dog1)
// 定義一個類型可以為接口類型的變量
// 實際上所有實現(xiàn)類都可以賦值給這個對象
var animal Animal3 // 模糊的 -- 具體化,將具體的實現(xiàn)類賦值給他,才有意義
animal = dog1
//接口是無法使用實現(xiàn)類的屬性的
test2(animal)
}
// Animal 接口
func test2(a Animal3) {
a.eat()
a.sleep()
}
接口的實現(xiàn)類都擁有多態(tài)特性:除了自己本身還是他對應(yīng)接口的類型。
3. 空接口
空接口interface{}不包含任何方法,因此任何類型都實現(xiàn)了空接口??战涌诳梢员灰暈槟苎b入任意數(shù)量、任意數(shù)據(jù)類型的數(shù)據(jù)容器。
因此空接口可以存儲任何的類型
空接口不好記,因此在新版本go中起了個名字,叫any
interface{} == any 之所以我們的fmt.Println能打印所有東西,就是因為它傳入的參數(shù)就是any,而any的類型就是空接口

點擊any進去看看,就是空接口

package main
import "fmt"
// A 定義空接口
type A interface{}
// Dogg 所有結(jié)構(gòu)體都實現(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: "旺財"}
var a3 A = 1
var a4 A = "景天科技苑"
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)
testNow(a1)
// map結(jié)合空接口,就可以存儲任何類型數(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ù)組里面的值默認是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. 接口嵌套
接口可以嵌套其他接口,即一個接口可以繼承多個別的接口。這時,如果要實現(xiàn)這個接口,必須實現(xiàn)它繼承的所有接口的方法。
package main
import (
"fmt"
)
type AA interface {
test1()
}
type BB interface {
test2()
}
// CC 接口嵌套 CC : test1()/test2()/test3()
// 如果要實現(xiàn)接口CC,那么需要實現(xiàn)這個三個方法。那這個對象就有3個接口可以轉(zhuǎn)型。
type CC interface {
AA // 導入AA接口中的方法
BB
test3()
}
// Dog7 編寫一個結(jié)構(gòu)體實現(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()
// 接口對象只能調(diào)用自己接口里面的方法
var a AA = dog
a.test1()
//a.test2() // 向上轉(zhuǎn)型之后只能調(diào)用它自己對應(yīng)的方法
var b BB = dog
b.test2()
//c三個方法都可以調(diào)用
var c CC = dog
c.test1()
c.test2()
c.test3()
}
5. 接口斷言
接口斷言用于檢查接口變量是否持有特定類型的值,并獲取該值。被斷言的對象必須是接口類型,否則會報錯
它有兩種形式:不安全斷言和類型安全的斷言。
不安全斷言
instance := 接口對象.(實際類型)
如果不滿足類型斷言,程序?qū)l(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")
assertsString(true) // panic: interface conversion: interface {} is bool, not string
}
// 判斷一個變量是不是string類型的
func assertsString(i interface{}) {
// 如果斷言失敗,則會拋出 panic 恐慌,程序就會停止執(zhí)行。
s := i.(string)
fmt.Println(s)
}
類型安全的斷言
instance, ok := 接口對象.(實際類型)
語法:t,ok:= i.(T) ok 隱藏返回值,如果斷言成功 ok就是true、否則就是false
如果斷言失敗,ok將會是false,而instance將會是類型的零值,并且不會觸發(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("中國")
}
// 斷言失敗的情況,我們希望程序不會停止。
func assertsInt(i any) {
r, ok := i.(int)
if ok {
fmt.Println("是我們期望的結(jié)果 int")
fmt.Println(r)
} else {
fmt.Println("不是我們期望的結(jié)果,無法執(zhí)行預期操作")
}
}
多個預期結(jié)果判斷
通過switch來判斷 switch i.(T)
i 必須是接口類型
i.(type)必須在switch中使用

package main
import "fmt"
// 通過switch來判斷 switch i.(T)
type I interface{}
// 如果斷言的類型同時實現(xiàn)了switch 多個case匹配,默認使用第一個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 // 沒有初始化空接口時,默認值為 nil類型 不屬于I類型
var i2 I = 1 // 只有賦值了之后,才是對應(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang Gin框架實現(xiàn)文件下載功能的示例代碼
本文主要介紹了Golang Gin框架實現(xiàn)文件下載功能的示例代碼,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12
Golang實現(xiàn)請求限流的幾種辦法(小結(jié))
這篇文章主要介紹了Golang實現(xiàn)請求限流的幾種辦法(小結(jié)),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10
Golang中常見的三種并發(fā)控制方式使用小結(jié)
這篇文章主要為大家詳細介紹了如何對goroutine并發(fā)行為的控制,在Go中最常見的有三種方式:sync.WaitGroup、channel和Context,下面我們就來看看他們的具體使用吧2024-01-01

