✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑
文章目录
接口与多态
1. 接口
1. 接口的定义
1、Go语言提供了接口数据类型。
2、接口就是把一些共性的方法集合在一起定义。
3、如果有实现类将接口定义的方法全部实现了,那么就代表实现了这个接口
4、隐式实现 Go ,假设A实现了B接口中的所有方法,不需要显示声明
5、接口是方法的定义集合,不需要实现具体的方法内容。名字约束
在Go语言中,接口(Interface)是一个重要的特性,它允许我们定义一组方法但不实现它们,任何类型只要实现了这些方法,就被认为是实现了该接口。
接口体现了程序设计的多态、高内聚、低耦合的思想,是实现面向对象编程中多态性的关键工具。
接口通过interface关键字定义,它是一组方法的集合。接口中的方法没有实现体,即它们没有具体的实现代码。一个类型只要实现了接口中的所有方法,就认为该类型实现了该接口。
如果一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的
2. 接口应用代码示例
接口的基本语法如下:
type 接口名 interface{
方法名1(参数列表1) 返回值列表1
方法名2(参数列表2) 返回值列表2...}
package main
import("fmt")// 接口: USB、typec、插座// 1、Go语言提供了接口数据类型。// 2、接口就是把一些共性的方法集合在一起定义。// 3、如果有实现类将接口定义的方法全部实现了,那么就代表实现了这个接口// 4、隐式实现 Go ,假设A实现了B接口中的所有方法,不需要显示声明// 5、接口是方法的定义集合,不需要实现具体的方法内容。名字约束// USB 接口的定义 interface 来定义,方法太多了,要归类,方法的集合type USB interface{// 接口,方法的集合input()// 输入方法output()// 输出方法}// Mouse 结构体type Mouse struct{
name string}// 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口func(mouse Mouse)output(){
fmt.Println(mouse.name,"鼠标输出")}func(mouse Mouse)input(){
fmt.Println(mouse.name,"鼠标输入")}// 接口调用测试functest(u USB){
u.input()
u.output()}funcmain(){// 通过传入接口实现类来进行调用
m1 := Mouse{name:"罗技"}// test 参数 USB 类型,如果一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的test(m1)//也可以单独测试接口//m1.input()
k1 := KeyBoard{name:"雷蛇"}test(k1)// 定义高级类型 k1就升级了 KeyBoard --> USB 向上转型var usb USB
usb = k1
fmt.Println(usb)// 接口是无法使用实现类的属性的//fmt.Println(usb.name)}// KeyBoard 结构体type KeyBoard struct{
name string}// 结构体实现了接口的全部方法就代表实现了这个接口,否则不算实现这个接口func(key KeyBoard)output(){
fmt.Println(key.name,"键盘输出")}func(key KeyBoard)input(){
fmt.Println(key.name,"键盘输入")}
带有参数和返回值的接口
package main
import"fmt"// Tongxin 定义接口type Tongxin interface{//定义带有参数和返回值的方法dadianhua(youdian bool)stringjieidanhua(youdian bool)string}// People 定义结构体type People struct{
name string
age int
phone string}// 实现接口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("接电话时手机没电了")}}// 接口测试,有传参,有返回值functestdianhua(phone Tongxin){
str1 := phone.dadianhua(false)
str2 := phone.jieidanhua(true)
fmt.Println(str1, str2)}funcmain(){//创建对象
p := People{"jingtian",18,"18898985898"}//如果一个结构体实现了这个接口所有的方法,那这个结构体就是这个接口类型的testdianhua(p)}
2. 模拟多态
多态是指相同的接口(方法)可以表现出不同的行为。在Go语言中,通过接口实现多态。
在Go语言中,接口定义了一组方法的集合,但不实现它们,而是由具体的类型来实现这些方法。
任何实现了接口中所有方法的类型都被视为该接口的实现。接口是Go语言中实现多态性的关键。
多态:一个事务有多种形态
父类:动物
子类:猫
子类:狗
猫和狗是多态的,他们既可以是自己,也可以是动物,这个就是多态,一个事务有多种形态
Go语言中多态的实现
定义接口
首先,我们需要定义一个接口,该接口包含了一组需要被实现的方法。例如,我们可以定义一个Shape接口,用于计算不同形状的面积。
type Shape interface{Area()float64}
在这个接口中,我们定义了一个Area()方法,该方法返回一个float64类型的值,表示形状的面积。
实现接口
接下来,我们需要定义具体的类型来实现这个接口。这些类型将提供Area()方法的具体实现。
矩形
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
}
使用接口进行多态调用
现在,我们可以使用Shape接口来创建不同类型的形状对象,并通过接口进行多态调用。
funcmain(){
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
}funcmain(){
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())}}
在上面的代码中,我们创建了一个shapes切片,该切片包含了不同类型的形状对象(矩形和圆形)。
然后,我们遍历shapes切片,并通过Shape接口调用Area()方法。由于这两种形状都实现了Shape接口,因此多态性使我们能够以一致的方式调用它们的Area()方法。
多态案例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")}// 多态funcmain(){// Dog 两重身份:1、Dog 2、Animal ,多态
dog1 := Dog3{name:"旺财"}
dog1.eat()
dog1.sleep()// Dog 也可以是 Animaltest2(dog1)// 定义一个类型可以为接口类型的变量// 实际上所有实现类都可以赋值给这个对象var animal Animal3 // 模糊的 -- 具体化,将具体的实现类赋值给他,才有意义
animal = dog1
//接口是无法使用实现类的属性的test2(animal)}// Animal 接口functest2(a Animal3){
a.eat()
a.sleep()}
接口的实现类都拥有多态特性:除了自己本身还是他对应接口的类型。
3. 空接口
空接口interface{}不包含任何方法,因此任何类型都实现了空接口。空接口可以被视为能装入任意数量、任意数据类型的数据容器。
因此空接口可以存储任何的类型
空接口不好记,因此在新版本go中起了个名字,叫any
interface{}== any
之所以我们的fmt.Println能打印所有东西,就是因为它传入的参数就是any,而any的类型就是空接口
点击any进去看看,就是空接口
package main
import"fmt"// A 定义空接口type A interface{}// Dogg 所有结构体都实现了空接口Atype Dogg struct{
name string}type Catt struct{
name string}functestNow(a A){
fmt.Println(a)}// 可以指定定义空接口// // any is an alias for interface{} and is equivalent to interface{} in all ways.// type any = interface{}// 可以传入任何东西functestNow2(temp interface{}){}funcmain(){//A类型可以是任何类型var a1 A = Catt{name:"喵喵"}var a2 A = Dogg{name:"旺财"}var a3 A =1var a4 A ="景天科技苑"
fmt.Println(a1)
fmt.Println(a2)
fmt.Println(a3)
fmt.Println(a4)testNow(a1)// map结合空接口,就可以存储任何类型数据
map1 :=make(map[string]interface{})
map1["name"]="dajiang"
map1["age"]=18
fmt.Println(map1)// slice,切片定义成空接口类型,也可以存放任何类型数据
s1 :=make([]any,0,10)
s1 =append(s1,1,"12312",false, a1, a2)
fmt.Println(s1)//数组空接口,数组里面的值默认是nil,也可以存放任何数据类型var arr [4]interface{}
fmt.Println(arr)
arr[0]=3
arr[1]="2"
arr[2]= s1
arr[3]=true
fmt.Println(arr)}
4. 接口嵌套
接口可以嵌套其他接口,即一个接口可以继承多个别的接口。这时,如果要实现这个接口,必须实现它继承的所有接口的方法。
package main
import("fmt")type AA interface{test1()}type BB interface{test2()}// CC 接口嵌套 CC : test1()/test2()/test3()// 如果要实现接口CC,那么需要实现这个三个方法。那这个对象就有3个接口可以转型。type CC interface{
AA // 导入AA接口中的方法
BB
test3()}// Dog7 编写一个结构体实现接口CCtype Dog7 struct{}func(dog Dog7)test1(){
fmt.Println("test1")}func(dog Dog7)test2(){
fmt.Println("test2")}func(dog Dog7)test3(){
fmt.Println("test3")}funcmain(){// dog 拥有4种形态: Dog7 、CC 、 BB 、 AAvar dog Dog7 = Dog7{}
dog.test1()
dog.test2()
dog.test3()// 接口对象只能调用自己接口里面的方法var a AA = dog
a.test1()//a.test2() // 向上转型之后只能调用它自己对应的方法var b BB = dog
b.test2()//c三个方法都可以调用var c CC = dog
c.test1()
c.test2()
c.test3()}
5. 接口断言
接口断言用于检查接口变量是否持有特定类型的值,并获取该值。被断言的对象必须是接口类型,否则会报错
它有两种形式:不安全断言和类型安全的断言。
不安全断言
instance := 接口对象.(实际类型)
如果不满足类型断言,程序将发生panic报错。
package main
import"fmt"// 断言 t := i.(T) t:t就是i接口是T类型的 i:接口 T:类型// 语法:t,ok:= i.(T) ok 隐藏返回值,如果断言成功 ok就是true、否则就是falsefuncmain(){//assertsString("11111111111")assertsString(true)// panic: interface conversion: interface {} is bool, not string}// 判断一个变量是不是string类型的funcassertsString(i interface{}){// 如果断言失败,则会抛出 panic 恐慌,程序就会停止执行。
s := i.(string)
fmt.Println(s)}
类型安全的断言
instance, ok := 接口对象.(实际类型)
语法:t,ok:= i.(T) ok 隐藏返回值,如果断言成功 ok就是true、否则就是false
如果断言失败,ok将会是false,而instance将会是类型的零值,并且不会触发panic。
接口断言代码示例
package main
import"fmt"// 断言 t := i.(T) t:t就是i接口是T类型的 i:接口 T:类型// 语法:t,ok:= i.(T) ok 隐藏返回值,如果断言成功 ok就是true、否则就是falsefuncmain(){//assertsString("11111111111")assertsInt("中国")}// 断言失败的情况,我们希望程序不会停止。funcassertsInt(i any){
r, ok := i.(int)if ok {
fmt.Println("是我们期望的结果 int")
fmt.Println(r)}else{
fmt.Println("不是我们期望的结果,无法执行预期操作")}}
多个预期结果判断
通过switch来判断 switch i.(T)
i 必须是接口类型
i.(type)必须在switch中使用
package main
import"fmt"// 通过switch来判断 switch i.(T)type I interface{}// 如果断言的类型同时实现了switch 多个case匹配,默认使用第一个case// 所以要把范围更小的匹配放前面functestAssert(i interface{}){// switch i.(type) 接口断言//i.(type)必须在switch中使用switch i.(type){casestring:
fmt.Println("变量为string类型")caseint:
fmt.Println("变量为int类型")casenil:
fmt.Println("变量为nil类型")casemap[string]int:
fmt.Println("map类型")caseinterface{}:
fmt.Println("变量为interface{}类型")//空接口与I一样case I:
fmt.Println("变量为I类型")// .....default:
fmt.Println("未知类型")}}funcmain(){testAssert("string")testAssert(1)var i I // 没有初始化空接口时,默认值为 nil类型 不属于I类型var i2 I =1// 只有赋值了之后,才是对应的类型testAssert(i)testAssert(i2)//map类型
j :=make(map[string]int)testAssert(j)}
版权归原作者 景天科技苑 所有, 如有侵权,请联系我们删除。