接口的概念和定义
在Go语言中接口(interface)是一种抽象的类型,interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则),不关心属性(传进来的数据是什么类型),只关心行为(要实现的方法),也就是说判断一个数据类型是不是接口类型,主要看这个数据类型的方法在接口类型中有没有相同的方法。
接口的定义格式如下:
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
复制
接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
实现接口的条件:
一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。
那么问题来了:
情况1:
如果对象只包含部分的接口中的方法,对象没有其他的方法了,这个会报错
如果对象只包含部分的接口中的方法,对象还有其他的的方法,这个也会报错
情况2:
包含了所有的接口的方法外还有其他的方法:这个可以正常执行
package main
import "fmt"
type cat struct {
name string
spice string
}
func (c cat) attribute(){
fmt.Printf("%s的品种是%s\n",c.name,c.spice)
}
func (c cat ) move(){
fmt.Println("%s在跑动",c.name)
}
type Animaler interface {
attribute()
eat() //注释掉折行代码,代码就不会报错
}
func main(){
var a Animaler
b := cat{"bubu","英短"}
a = b
a.attribute()
}
报错:cannot use b (type cat) as type Animaler in assignment:
cat does not implement Animaler (missing eat method)
复制
总结:
除了方法名和参数必须一致外,对象只能比接口的方法多或者一样多,不能比接口的方法少。
值类型和指针类型实现接口的不同
使用值类型接收者实现接口:类型的值和类型的指针都能够保存在接口变量中,因为Go语言中有对指针类型变量求值的语法糖,b指针c内部会自动求值*b
package main
import "fmt"
type cat struct {
name string
spice string
}
func (c cat) attribute(){
fmt.Printf("%s的品种是%s\n",c.name,c.spice)
}
type Animaler interface {
attribute()
}
func main(){
var a Animaler
var c Animaler
b := cat{"bubu","英短"}
a = &b
c = b
a.attribute()
c.attribute()
}
输出结果:
bubu的品种是英短
bubu的品种是英短
复制
使用指针接收者实现接口:只有类型指针能保存在接口变量中
package main
import "fmt"
type cat struct {
name string
spice string
}
func (c *cat) attribute(){
fmt.Printf("%s的品种是%s\n",c.name,c.spice)
}
type Animaler interface {
attribute()
}
func main(){
var a Animaler
var c Animaler
b := cat{"bubu","英短"}
a = &b //不会报错
c = b //报错
a.attribute()
c.attribute()
}
编译报错:
在main函数中 c=b 这段代码报错:cannot use b (type cat) as type Animaler in assignment: cat does not implement Animaler (attribute method has pointer receiver)
复制
类型与接口的关系
1.一个类型可以同时实现多个接口,而接口彼此独立,不知道对方的实现。
2.多个类型还可以实现同一个接口。
3.一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。
接口嵌套
接口与接口间可以通过嵌套创造出新的接口
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
move()
}
// 接口嵌套
type animal interface {
Sayer
Mover
}
复制
空接口
空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。空接口类型的变量可以存储任意类型的变量。
空接口一般不需要提前定义。
空接口的作用:
1.空接口类型可以作为函数的参数
2.空接口可以作为map的value
package main
import "fmt"
func main(){
var x interface{}
x = "hello world"
fmt.Println(x)
x = 100
fmt.Println(x)
var m = make(map[string]interface{},16)
m["name"] = "xiaoqiang"
m["age"] = "8"
m["hobby"] = []string{"football","basketball"}
fmt.Println(m)
}
输出结果:
hello world
100
map[age:8 hobby:[football basketball] name:xiaoqiang]
复制
关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。