go-接口
接口
接口定义一个对象的行为,是一系列方法的集合
声明
接口中定义的方法是接口类型的一部分
同结构体一样,由于接口内封装的方法不同(数量,方法名和对应的类型)而导致每一种接口类型都不同,所以通常用type关键字重命名
定义接口
// 关键字 interface
type DuckInterface interface {
run() // 接口规定的方法,包括形参,返回值
speak()
}
声明接口变量
接口是引用类型,即接口变量的零值是nil
// 基于定义好的接口类型声明一个接口变量
var d DuckInterface
// 接口变量的值,可以是实现了这个接口的任意类型的变量
实现
只要一个对象绑定了接口规定的所有方法,那么就表明该对象实现了该接口
// 以结构体为例
// 声明一个接口类型
type StudentInterface interface {
learn()
write()
}
// 声明两个结构体类型
type A struct {
name string
age int
}
type B struct {
name string
age int
hobby string
}
// 给结构体绑定方法,实现接口
// 指针接收器
func (s *A) write() {
fmt.Println(s.name, "开始写作业")
}
func (s *A) learn() {
fmt.Println(s.name, "开始学习")
}
// 值接收器
func (s B) write() {
fmt.Println(s.name, "开始写作业")
}
func (s B) learn() {
fmt.Println(s.name, "开始学习")
}
接口值
类型A实现了接口I,那么类型A的变量a就是一个I接口,它可以被赋值给接口I的变量i
1、变量a可以访问它本身的所有的属性和方法
2、a被赋值给i后,i就可以调用A类型实现接口的方法,但是不能访问a的其他方法和属性
3、i数据结构interface实现原理
注意:
A绑定接口方法时,若是使用指针接收器绑定,那么i就只能赋值为a的指针;
若是使用值接收器绑定接口方法,那么i既可以赋值为a,也可以赋值为a的指针
// 借助上一段代码进行演示
func main() {
var a = A{"小红", 19}
var b = B{"小刘", 19, "daPao",}
var i StudentInterface
i = &a // 只能接收&a
i.learn()
i = b // 可以接收b,也可以接收&b
i.learn()
}
类型断言
接口i赋值为&a,&a就叫做接口i的动态值
通过i.(T),可以判断i的动态值的类型是不是T,若是,会返回动态值,否则就会抛出panic异常,这就是接口的类型断言
// 借助上一段代码进行演示
func main() {
var a = A{"小红", 19}
// var b = B{"小刘", 19, "daPao",}
var i StudentInterface
i = &a
a1 := i.(*A) // 断言i接收的是一个A类型的变量:正确
fmt.Println(a)
b := i.(B) // 断言i接收的是一个B类型的变量:错误,程序会报错
}
类型选择
i.(type) 直接得到接口i的动态值
这种语法,i只能是接口变量,并且只能在switch语句中使用
switch中,case会做对接口i的动态值的类型验证,验证通过就执行case代码
func testInterface() {
//var a = A{"小红", 19}
var b = B{"小刘", 19, "daPao",}
var i StudentInterface
// i可以接收b,&b和a,但是不能接收a
i = &b
switch v := i.(type) { // 此处v就是&b
case *A:
fmt.Printf("i接收的是一个%T类型的数据,%v", v,*v)
case B:
fmt.Printf("i接收的是一个%T类型的数据,%v",v,v)
case *B:
fmt.Printf("i接收的是一个%T类型的数据,%v",v,*v)
default:
fmt.Printf("i接收的是一个未知类型%T的数据,%v",v,v)
}
fmt.Println(b)
}
空接口与匿名空接口
空接口
// 接口内没有定义任何方法
type StudentInterface interface {}
// 所有的数据类型都实现了空接口:空接口变量可以接受任意类型的数据
var a StudentInterface
a = "hello"
a = 123
a = [3]int{1,2,3}
匿名接口
// 即不再使用type关键之命名接口类型,直接使用关键字interface声明一个接口变量
var i1 interface{}
var i2 interface{
speak()
learn()
}
i3 := interface{
walk()
}
匿名空接口
var i interface{}
// i可以接收任意类型的变量
接口嵌套
声明
// 即type定义一个接口时,除了定义接口方法外,也可以定义一个接口类型
type PeopleInterface interface{
speak()
walk()
}
type StudentInterface {
learn()
write()
PeopleInterface // 嵌套了接口PeopleInterface
}
// StudentInterface相当于
type StudentInterface {
learn()
write()
speak()
walk()
}
断言接口
i.(T)判断接口i的动态值是不是实现了接口类型T
与断言接口动态值的数据类型不同:
1、进行断言的接口类型T只要是个接口类型即可,不一定要在接口i的接口类型中嵌套过
2、会返回两个值:
断言成功,第一个值为接口类型T的一个变量(它的动态值仍是接口i的动态值),第二个值为布尔值true;
断言失败,第一个值为接口类型的零值nil,第二个值为布尔值false
func testInterface(){
//var a = A{"小红", 19}
var b = B{"小刘", 19, "daPao",}
var i StudentInterface
i = &b
p, ok := i.(PeopleInterface)
fmt.Println(p,ok) // p是一个接口,动态值为&b
}
非侵入式
嵌套在内的接口相当于面向对象中的父类
侵入式接口和非侵入式接口
侵入式接口:修改,删除,对子类有影响,如java
非侵入式接口:修改,删除,对子类有没有影响,如go,Python

浙公网安备 33010602011771号