golang 接口inferface和多态
1.接口快速入门
基本介绍:
inferface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。到某个自定义类型(比如结构体Phone)要使用的时候,在根据具体情况把这些方法写出来。interface是引用类型。引用类型包含:指针,slice切片,map,管道chan,interface都是引用类型。
基本语法:
type 接口名 interface{ func {t 自定义类型} method1{参数列表} 返回值列表{
method1(参数列表) 返回值列表 实现接口的所以方法 //方法实现
method2(参数列表) 返回值列表 --------------> }
... func {t 自定义类型} method2{参数列表} 返回值列表{
} //方法实现
}
//...
小结说明:
1)接口里所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想
2)Golang中的接口,不需要显式的实现,只要一个变量,含有接口类型的所有方法,那么这个变量就实现这个接口。因此,Golang中没有implement这样的关键字。
package main import "fmt" type Usb interface { //声明了两个没有实现的方法 Start() Stop() } //手机 type Phone struct { } //让Phone实现Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作") } func (p Phone) Stop() { fmt.Println("手机停止工作") } //相机 type Camera struct { } //让Camera实现Usb接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作") } func (c Camera) Stop() { fmt.Println("相机停止工作") } //计算机 type Computer struct { } //计算机方法Working,接收Usb接口类型变量 //只要实现了Usb接口,所谓实现Usb接口, 就是指实现了Usb接口声明的所有方法(Phone Camera) func (C Computer) Working(usb Usb) { //通过usb接口变量来调用Start和Stop方法 usb.Start() usb.Stop() } func main() { computer := Computer{} phone := Phone{} camera := Camera{} //关键点 computer.Working(phone) computer.Working(camera) }
2.注意事项和细节:
1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)。
2)接口中所有的方法都没有方法体,即都是没有实现的方法。
3)在Golang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋值给接口类型。
5)只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
package main import "fmt" type AInterface interface { Say() } type Student struct { } func (stu Student) Say() { fmt.Println("stu say()") } type integer int func (i integer) Say() { fmt.Println("integer Say i =", i) } func main() { stu := Student{} var a AInterface = stu a.Say() var i integer = 10 var b AInterface = i b.Say() }
6)一个自定义类型可以实现多个接口
package main import "fmt" type AInterface interface { Say() } type BInterface interface { Hello() } type Student struct { } func (stu Student) Say() { fmt.Println("stu say()") } func (stu Student) Hello() { fmt.Println("stu Hello()") } func main() { stu := Student{} var a AInterface = stu var b BInterface = stu a.Say() b.Hello() }
7)Golang接口中不能有任何变量
8)一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现。
package main import "fmt" type Binterface interface { test01() } type Cinterface interface { test02() } type Ainterface interface { Binterface Cinterface test03() } type Student struct { } func (stu Student) test01() { fmt.Println("test01()") } func (stu Student) test02() { fmt.Println("test02()") } func (stu Student) test03() { fmt.Println("test03()") } func main() { var stu Student var a Ainterface = stu a.test01() }
9)interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
10)空接口interface{}没有任何方法,所以类型都实现了空接口,可以把任何一个变量都赋给空接口类型。
11)一个接口中有两个同名的方法会报错。如A接口中有B,C接口,且B,C接口中都有test01(),这样就会报错。
3.接口实例
实现对Hero结构体切片的排序:sort.Sort(data interface)
package main import ( "fmt" "math/rand" "sort" ) //实现对Hero结构体切片的排序:sort.Sort(data interface) //1.声明Hero结构体 type Hero struct { Name string Age int } //2.声明Hero结构体切片类型 type HeroSlice []Hero //实现接口的三个方法:Len(),Less(i int, j int),Swap(i int, j int) func (hs HeroSlice) Len() int { return len(hs) } func (hs HeroSlice) Less(i int, j int) bool { return hs[i].Age < hs[j].Age } func (hs HeroSlice) Swap(i int, j int) { hs[i], hs[j] = hs[j], hs[i] } func main() { //定义切片 hs := HeroSlice{} //赋值切片 for i := 0; i < 10; i++ { hero := Hero{} hero.Name = fmt.Sprintf("hero~%d", rand.Intn(100)) hero.Age = rand.Intn(100) hs = append(hs, hero) } //排序 sort.Sort(hs) for _, v := range hs { fmt.Println(v) } }
4.接口和继承
小结
1)当A结构体继承了B结构体,那么A结构体就自动继承了B结构体的字段和方法,并且可以直接使用
2)当A结构体需要扩展功能,同事不希望去破坏继承关系,则可以去实现某个接口即可,因此我们可以认为:实现接口是对继承机制的补充。
3)继承的价值主要在于:解决代码的复用性和可维护性;接口的价值主要在于:设计,设计好各种规范(方法),让其他自定义类型去实现这些方法。
4)接口比继承更加灵活:继承是满足is -a的关系,而接口只需要满足like-a的关系。
5)接口在一定程度上实现代码解耦。
package main import "fmt" type Monkey struct { Name string } //LittleMonkey继承Monkey type LittleMonkey struct { Monkey } func (mo *Monkey) Climbing() { fmt.Printf("%v 会爬树\n", mo.Name) } //定义接口BirdAble,FishAble来扩展结构体的方法 type BirdAble interface { Flying() } type FishAble interface { Swapping() } func (limo *LittleMonkey) Flying() { fmt.Printf("%v 会飞\n", limo.Name) } func (limo *LittleMonkey) Swapping() { fmt.Printf("%v 会游泳\n", limo.Name) } func main() { limo := LittleMonkey{} limo.Name = "悟空" limo.Climbing() limo.Flying() limo.Swapping() }
5.多态
变量(实例)具有多种形态。面向对象的第三大特征,在Go语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现出不同的形态。
多态参数:
Usb既可以接收手机变量,又可以接收相机变量,就体现了Usb接口的多态性。
多态数组:
给Usb数组中,存放Phone结构体和 Camera结构体变量。因为有了多态数组才可以存放不同类型是元素,普通数组只能存放一种类型的元素。
package main import "fmt" type Usb interface { //声明了两个没有实现的方法 Start() Stop() } //手机 type Phone struct { Name string } //让Phone实现Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作") } func (p Phone) Stop() { fmt.Println("手机停止工作") } //相机 type Camera struct { Name string } //让Camera实现Usb接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作") } func (c Camera) Stop() { fmt.Println("相机停止工作") } func main() { //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量 var usbArr [3]Usb usbArr[0] = Phone{"手机1"} usbArr[1] = Phone{"手机2"} usbArr[2] = Camera{"相机"} fmt.Println(usbArr) }
6.类型断言
基本概念:
类型断言,由于接口是一般类型,不知道具体类型,如果要转成具体的类型,就需要使用类型断言。
var x interface{} var b float32 = 1.1 x = b //空接口,可以接收任意类型 //x=>float32【使用类型断言】 y := x.(float32) fmt.Printf("y的类型是%T 值是=%v", y, y)
小结:
在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的空接口指向的就是断言类型。
如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic
var x interface{} var b float32 = 1.1 x = b //空接口,可以接收任意类型 //x=>float32【使用类型断言】 y, ok := x.(float64) if ok { fmt.Println("convert success") fmt.Printf("y的类型是%T 值是=%v", y, y) } else { fmt.Println("convert fail") } fmt.Println("代码继续执行")
实例1:
对前面的Usb接口做改进:
给Phone结构体增加一个特有的方法call(),当Usb接口接收的是Phone变量时,还需要调用call方法。
package main import ( "fmt" ) type Usb interface { //声明了两个没有实现的方法 Start() Stop() } //手机 type Phone struct { Name string } //让Phone实现Usb接口的方法 func (p Phone) Start() { fmt.Println("手机开始工作") } func (p Phone) Stop() { fmt.Println("手机停止工作") } func (p Phone) Call() { fmt.Println("手机在打电话") } //相机 type Camera struct { Name string } //让Camera实现Usb接口的方法 func (c Camera) Start() { fmt.Println("相机开始工作") } func (c Camera) Stop() { fmt.Println("相机停止工作") } //计算机 type Computer struct { } //计算机方法Working,接收Usb接口类型变量 //只要实现了Usb接口,所谓实现Usb接口, 就是指实现了Usb接口声明的所有方法(Phone Camera) func (C Computer) Working(usb Usb) { //通过usb接口变量来调用Start和Stop方法 usb.Start() //如果usb是指向Phone结构体变量,则还需要使用Call方法 //使用类型断言[注意使用的好处] if phone, ok := usb.(Phone); ok { phone.Call() } usb.Stop() } func main() { //定义一个Usb接口数组,可以存放Phone和Camera的结构体变量 var usbArr [3]Usb usbArr[0] = Phone{"手机1"} usbArr[1] = Phone{"手机2"} usbArr[2] = Camera{"相机"} var computer Computer for _, v := range usbArr { computer.Working(v) } }
实例2:
写一个函数,循环判断传入参数的类型
type Student struct { } func TypeJudge(item ...interface{}) { for index, x := range item { switch x.(type) { case bool: fmt.Printf("第%v个参数是bool类型,值是%v\n", index, x) case float32: fmt.Printf("第%v个参数是float32类型,值是%v\n", index, x) case float64: fmt.Printf("第%v个参数是float64类型,值是%v\n", index, x) case int, int32, int64: fmt.Printf("第%v个参数是整数类型,值是%v\n", index, x) case string: fmt.Printf("第%v个参数是string类型,值是%v\n", index, x) case Student: fmt.Printf("第%v个参数是Student类型,值是%v\n", index, x) case *Student: fmt.Printf("第%v个参数是*Student类型,值是%v\n", index, x) default: fmt.Printf("第%v个参数的类型不确定,值是%v\n", index, x) } } } func main() { var n1 float32 = 1.1 var n2 float64 = 1.2 var n3 int32 = 30 var name string = "tom" n4 := 300 address := "北京" stu := Student{} stu2 := &Student{} TypeJudge(n1, n2, n3, name, n4, address, stu, stu2) }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?