栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Go语言

第十一章:接口与类型断言

Go语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

第十一章:接口与类型断言

接口是描述一系列方法的集合,目的是为了抽象,方便代码解耦

接口类型是一种抽象的类型,不会暴露它所代表的内部属性的规范,只会展示出它自己的方法,因此不能将接口类型实例化

Go的接口是非常灵活的,通过一种方式来声明对象的行为,谁实现了这个行为,就相当于实现了接口里面声明各种方法的集合,但接口本身不去实现这些方法所需要的一些操作,所以说是抽象的方法。

说白了就是接口只声明方法,具体怎么实现由类定义。接口没有数据字段,只有定义的方法

type Animal interface{
	Name() string
	Speak() string
}

type Cat struct{
}

func (cat Cat) Name() string{
	return "Cat"
}
func (cat Cat) Speak() string{
	return "喵喵喵"
}

结构体Cat实现了Animal接口的两个方法,因此我们认为Cat实现了Animal接口

鸭子类型(程序设计风格)

著名的鸭子类型:动态类型的一种风格,一个对象有效的语义,不是由继承自特定的类或实现的接口来决定,而是由当前方法和属性的集合决定。
鸭子测试:当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟也可以被称为鸭子。 Go语言通过接口实现了鸭子类型。
提示:一般来说,静态类型语言在编译时便以确定了变量的类型,Go语言的实现是在编译时判断变量的类型。

接口的创建和实现 接口的创建:

type 接口名 interface{
 Method()
}

type IDatabaser interface{
	Connect() error
	Disconnect() error
}

type Mysql struct{
	DBName string
	isConnect bool
}

type Redis struct{
	DBName string
}

func (mysql *Mysql) Connect() error {
	fmt.Println("Mysql Connct DB =>" + mysql.DBName)
	mysql.isConnect = true
	if mysql.isConnect {
		fmt.Println("Mysql Connct Success!")
	}else{
		return errors.New("Connct failure!")
	}
}

func (mysql *Mysql) Disconnct() error{
	fmt.Println("Mysql Disconnect Success!")
	return nil
}

func (redis *Redis)Conncet() error{
	fmt.Println("Redis Connct DB =>" + mysql.DBName)
	fmt.Println("Redis Connct Success!")
	return nil
}
func (redis *Redis)Disconnect() error{
	fmt.Println("Redis Disconnect Success!")
	return nil
}

func main(){
	var mysql = Mysql{DBName:"student"}
	fmt.Println("开始连接!")
	mysql.Connect
	fmt.Println("断开连接!")
	mysql.Disconnect

	var redis := Redis{DBName:"teacher"}
	fmt.Println("开始连接!")
	redis.Connect
	fmt.Println("断开连接!")
	redis.Disconnect
}

上面我们定义了接口,但是不断建立新的数据库类型就要实现接口的方法,有很多重复的代码!

面向接口编程,可以抽象出更简洁,更易于管理的代码。 Go多态就是通过接口实现的

func HandleDB(db IDatabaser){
	fmt.Println("开始连接!")
	db.Connect
	fmt.Println("断开连接!")
	db.Disconnect
}

func main(){
	mysql := Mysql{"DBname":"student"}
	HandleDB(&mysql)
	
	redis := Redis("DBName":"teacher")
	HandleDB(&redis)
}

HandleDB()函数只有一个,却能实现处理多个不同类型的数据 这也成为GO的多态。

一个类型可以实现多个接口,多个类型可以实现同一个接口。

接口赋值:

如果用户自定义类型实现了某个接口类型所声明的一组方法,那么这个用户定义的类型的值就可以赋给一个接口变量
var 接口变量名 接口类型 = &对象

存在两种赋值情况:
1.对象实例赋值给接口,注意只能将对象的指针赋值给接口变量
2.将一个接口赋值给另一个接口。若两个接口拥有同样的方法集,那么他们就是相同的,可以相互赋值,若接口A的方法集是接口B方法集的子集,那么接口B可以赋值给接口A,反之不成立。

type IDatabaser interface{
	Connect() error
	Disconnect() error
}

type IRediser interface{
	Connect() error
}
type Redis struct{
	DBName string
}

func (redis *Redis)Connct() error{
	fmt.Println("Redis Connct DB =>" + mysql.DBName)
	fmt.Println("Redis Connct Success!")
	return nil
} 
func (redis *Redis)Disconnect() error{
	fmt.Println("Redis Disconnect Success!")
	return nil
}

func main() {
	var idb IDatabaser = &Redis{DBName:"teacher"}
	
	var iredis IRediser
	iredis = idb
	iredis.Connect()
]
接口嵌入(继承):非侵入式的风格

侵入式接口:需要显式地创建一个类实现一个接口(例如Java要实现一个接口,不许使用implements显式声明)
非侵入式接口:无需显式地创建一个类实现接口

接口类型只嵌入接口类型,不能嵌入自身和其他数据类型,包括直接嵌入和间接嵌入。

type IPerson interface{
	Speak()
}
type IStudent interface{
	IPerson
	Study()
}
type ITeacher interface{
	IPerson
	Teach()
}

type Teacher struct{
	Name string
}

func (t *Teacher) Speak(){
	fmt.Println("My Name is ",t.Name)
}
func (t *Teacher) Teach(){
	fmt.Println(t.Name," is teaching!")
}

func main(){
	vat teacher = &Teacher{Name:"Mr Li"}
	teacher.Speak()
	teacher.Teach()
}

ITeacher接口嵌入了IPerson接口,可以理解继承了IPerson接口,都拥有了IPerson接口中的Speak()方法,但是需要结构体Teacher实现这两个方法。

非侵入式好处:

1、去掉繁杂的继承体系
2、实现类的时候只需关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理
3、不用为了实现一个接口导入一个包,多引用一个外部的包,意味着更多的耦合。这样更简洁,灵活,注重实用性。提高代码复用率(通过接口赋值实现新的接口)

空接口:保存值,空接口不包含任何方法,所以可以存储任意类型的数值。

从空接口取值:不能把空接口赋值到其他类型,若需如此,必须使用类型断言。

func main(){
	var a string = "abc"
	var i interface = a
	var b string = i.(string)     //简单断言(一旦不是string,就会抛出panic),建议使用下面的类型断言
	fmt.Println(b)
}
//结果为:
abc
类型断言:接口类型向普通类型的转换就是类型断言

语法: t,ok := X.(T)
判断X的类型是否是T,如果断言成功,ok为True,t的值为接口变量X的动态值,失败t为类型T的初始值。

如果断言类型T是一个接口类型,若检查成功,检查结果接口t转换为接口类型T,t的值为接口变量X的动态类型和动态值
这意味着对一个接口类型的断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大),但是他保护了接口值内部的动态类型和值的部分。

接口类型断言方式有两种:ok-pattern switch-type(断言的接口类型很多时使用)
type Person interface{
	Speak()
}
type Student struct{
	Name string
}

func (s Student) Speak(){
	fmt.Println("My name is ",s.Name)
}
func checkType(t interface{}, ok bool) {
	if ok{
		fmt.Println("断言成功")
	}else {
		fmt.Println("断言失败")
	}
	fmt.Printf("变量t的类型 = %T,值 = %v n",t ,t)
}
func main(){
	var student interface{} = Student{Name:"小明"}
	fmt.Println("第一次断言:")
	t0,ok := student.(string)
	checkType(t0,ok)
	fmt.Println("第二次断言:")
	t1,ok := student.(Person)
	checkType(t1,ok)
}
//结果为:
第一次断言:
断言失败!
变量t的类型 = string,值 = 
第二次断言:
断言成功!
变量t的类型 = main.Student,值 = {小明}   //空接口变为
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/991043.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号