我正在参加「掘金·启航计划」
1、接口
接口(interface)是一种类型,定义了一个对象的行为规范,是一组方法的集合,接口指定了类型应该具有的方法,类型决定了如何实现这些方法。
- 接口是一个或多个方法签名的集合
- 任何类型的方法集中只要拥有该接口对应的
全部方法签名,就表示它实现了该接口,无需在该类型上显示声明实现了哪个接口。 - 所谓对应方法,是指有相同名称、参数列表(不包括参数名)以及返回值。该类型还可以有其他方法。
- 接口只有方法声明,没有实现,没有数据字段。
- 接口可以匿名嵌入其他接口,或嵌入到结构体中。
- 对象赋值给接口时,会发生拷贝,而接口内部存储的是指向这个复制品的指针,既无法修改复制品的状态,也无法获取指针。
- 只有当接口存储的类型和对象都为nil时,接口才等于nil。
- 接口调用不会做receiver的自动转换。
- 接口同样支持匿名字段方法。
- 接口也可实现类似oop中的多态。
- 空接口可以作为任何类型数据的容器。
- 一个类型可以实现多个接口。
- 接口命名习惯以
er结尾。
2、接口的定义
每个接口由数个方法组成,接口的定义格式如下:
type 接口类型名 interface {
方法名1(参数列表1) 返回值列表1
方法名2(参数列表2) 返回值列表2
...
}
其中:
- 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如定义动物接口Animaler。
- 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
示例代码:
package main
import "fmt"
type Animaler interface {
Say()
}
type Dog struct {
}
type Cat struct {
}
func (d Dog) Say() {
fmt.Println("dog say 汪 汪 汪 ~")
}
func (c Cat) Say() {
fmt.Println("cat say 喵 喵 喵 ~")
}
func main() {
var animal Animaler
animal = Dog{} //值类型接收者
animal.Say()
animal = new(Cat)
animal.Say()
animal = &Dog{} //指针类型接收者
animal.Say()
}
3、值接收者和指针接收者实现接口的区别
3.1、值接收者实现接口
type Animaler interface {
Say()
}
type Dog struct {
}
func (d Dog) Say {
fmt.Println("dog say 汪 汪 汪 ~")
}
func main (){
var animal Animaler
animal = Dog{} //值类型接收者
animal.Say()
animal = &Dog{} //指针类型接收者
animal.Say()
}
使用值接收者实现接口后,不管是dog结构体还是还是结构体指针*Dog类型的变量都可以赋值给该接口变量。
3.2、指针接收者实现接口
type Animaler interface {
Say()
}
type Dog struct {
}
func (d *Dog) Say {
fmt.Println("dog say 汪 汪 汪 ~")
}
func main (){
var animal Animaler
animal = Dog{} //值类型接收者 会报错无法编译
animal.Say()
animal = &Dog{} //指针类型接收者
animal.Say()
}
指针类型接收者实现的接口,只能存储指针类型,否则会报错。
4、类型与接口的关系
4.1、一个类型实现多个接口
一个类型可以同时实现多个接口,而接口间相互独立,不知到对方的实现。
4.2、多个类型实现同一接口
不同类型可以实现同一接口。
4.3、接口嵌套
接口与接口可以通过嵌套创造出新的接口。
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
move()
}
// 接口嵌套
type animal interface {
Sayer
Mover
}
5、空接口
5.1、空接口定义
空接口是指没有定义任何方法的接口,因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
var s interface{}
s = "test"
fmt.Printf("s type:%T, s value:%v\n", s, s) //s type:string, s value:test
5.2、空接口的应用
5.2.1、空接口作为函数的参数
使用空接口可以接收任意类型的函数参数。
func display(s interface{}) {
fmt.Println(s)
}
5.2.2、空接口作为map的值
使用空接口实现可以保存任意值得字典。
var arr = make(map[int]interface{}, 5)
arr[0] = "test"
arr[1] = 123
5.2.3、类型断言
空接口可以存储任意类型的值,如何获取其存储的具体数据呢?
一个接口的值是由一个具体类型和具体类型的值两部分组成。这两部分分别称为接口的动态类型和动态值。
想要判断空接口中的值这个时候就可以使用类型断言,语法如下:
value, ok := x.(Type)
- x表示类型为interface{}的变量
- Type表示断言x可能是的类型
该语法有两个返回值,第一个是x转化为Tyep类型后的变量,第二个值是一个布尔值,若为true表示断言成功,为false表示断言失败。
示例:
func main() {
var x interface{}
x = "test"
v, ok := x.(string)
if ok {
fmt.Println(v)
} else {
fmt.Println("类型断言失败")
}
}
如果断言多次需要写多个if判断,可以配合switch使用:
var s interface{}
s = "test"
switch v := s.(type) {
case string:
fmt.Printf("s is string, value is:%v\n", v)
case int:
fmt.Printf("s is int, value is:%d\n", v)
default:
fmt.Println("断言失败")
}