接口
接口(interface)定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。
接口类型
在Go语言中接口是一种类型,一种抽象的类型。
接口做的事情就像是定义一个协议(规则),只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)
接口的定义
Go语言提倡面向接口编程。每个接口由数个方法组成。
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
- 接口名:使用
type
将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er
,如有写操作的接口叫Writer
,有字符串功能的接口叫Stringer
等。接口名最好要能突出该接口的类型含义。 - 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
实现接口的条件
一个对象只要全部实现了接口的方法,那么就实现了这个接口
type dog struct {
name string
}
type cat struct{}
// 实现狗的 sayHi 方法
func (d dog) sayHi() {
fmt.Println("汪汪汪~~~")
}
// 实现猫的 sayHi 方法
func (c cat) sayHi() {
fmt.Println("喵喵喵~~~")
}
// sayer 接口
type sayer interface {
sayHi()
}
接口类型变量
接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer
类型的变量能够存储dog
和cat
类型的变量。
func main() {
var c sayer // 声明一个sayer类型的变量c
a := dog{name: "旺财"} //实例化一个dog
c = a // 将dog实例直接赋值给c
fmt.Printf("%#v\n", c) // main.dog{name:"旺财"}
c.sayHi() // 汪汪汪~~~
b := cat{} //实例化一个cat
c = b
b.sayHi() // 喵喵喵~~~
}
Go中描述的方法集
values Methods Receivers
--------------------------------------------------------------------
T (t T)
*T (t T)and (t *T)
从接收者角度来看方法集
Methods Receivers values
--------------------------------------------------------------------
(t T) T and *T
(t *T) *T
值接收者实现接口
type dog struct {
name string
}
// Mover 为动作接口
type Mover interface {
move()
}
func (d dog) move() {
fmt.Printf("%s在快速奔跑", d.name)
}
func main() {
var x Mover
d1 := dog{"旺财"}
x = d1
fmt.Println(x) // {旺财}
d2 := &dog{"富贵"}
x = d2
fmt.Println(x) // &{富贵}
x.move() // 富贵在快速奔跑
}
无论是dog结构体还是指针结构体的*dog类型变量都可以赋值给该接口变量。
指针接收者实现接口
func (d *dog) move() {
fmt.Printf("%s在快速奔跑", d.name)
}
func main() {
var x Mover
//d1 := dog{"旺财"}
//x = d1 // 编译不通过
d2 := &dog{"富贵"}
x = d2
fmt.Println(x) // &{富贵}
x.move() // 富贵在快速奔跑
}
此时接口实现的是*dog类型,直接将x传入是编译不通过的,此时只能是存储*dog的值。
一个类型实现多个接口
一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。
type dog struct {
name string
}
// Mover 为动作接口
type Mover interface {
move()
}
// Sayer 为说话接口
type Sayer interface {
sayHi()
}
func (d *dog) move() {
fmt.Printf("%s在快速奔跑\n", d.name)
}
func (d *dog) sayHi() {
fmt.Printf("%s在叫唤\n", d.name)
}
func main() {
var x Mover
d2 := &dog{"富贵"}
x = d2
x.move() // 富贵在快速奔跑
var y Sayer = &dog{"旺财"}
y.sayHi()
}
多个类型实现同一个接口
type dog struct {
name string
}
type car struct{
name string
}
// Mover 为动作接口
type Mover interface {
move()
}
func (c *car) move(){
fmt.Printf("%s在快速奔跑\n", c.name)
}
func (d *dog) move() {
fmt.Printf("%s在快速奔跑\n", d.name)
}
这个时候就不必关心是什么物体了,只要调用他们的move方法就可以了
func main() {
var x Mover
d1 := &dog{"富贵"}
x = d1
x.move() // 富贵在快速奔跑
var y Mover = &car{"兰博"}
y.move()
}
并且一个接口的方法,不一定需要由一个类型完全实现,接口的方法可以通过在类型中嵌入其他类型或者结构体来实现。
// WashingMachine 洗衣机
type WashingMachine interface {
wash()
dry()
}
// 甩干器
type dryer struct{}
// 实现WashingMachine接口的dry()方法
func (d dryer) dry() {
fmt.Println("甩一甩")
}
// 海尔洗衣机
type haier struct {
dryer //嵌入甩干器
}
// 实现WashingMachine接口的wash()方法
func (h haier) wash() {
fmt.Println("洗刷刷")
}
接口嵌套
接口也可以通过接口嵌套实现新接口
// Sayer 接口
type Sayer interface {
say()
}
// Mover 接口
type Mover interface {
move()
}
// 接口嵌套
type animal interface {
Sayer
Mover
}
空接口
空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口。
空接口类型的变量可以存储任意类型的变量。
func main() {
var x interface{}
s := "字符串"
x = s
fmt.Printf("type:%T value:%v\n", x, x)
i := 100
x = i
fmt.Printf("type:%T value:%v\n", x, x)
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x)
}
空接口的应用
空接口作为函数的参数
// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
空接口作为map的值
使用空接口实现可以保存任意值的字典
func main() {
var students = make(map[string]interface{}, 10)
students["name"] = "三藏"
students["age"] = 99
fmt.Println(students)
}
类型断言
想要判断空接口中的值这个时候就可以使用类型断言,其语法格式:
x.(T)
x 表示类型为interface{}的变量
T 表示断言x可能的类型
返回两个值,第一个值为x参数转换为T后的值,第二个值为布尔,真为True,假为False
func main() {
var x interface{}
x = "我是字符串"
v, ok:=x.(string)
if ok{
fmt.Println(v)
}else{
fmt.Println("断言失败")
}
}
使用switch做断言
func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}
判断是否实现了接口
type Reader interface {
Read()
}
type Writer interface {
Write()
}
type ReadWriter interface {
Reader
Writer
}
type File struct {
}
func (f *File) Read() {
fmt.Println("read data")
}
func (f *File) Write() {
fmt.Println("write data")
}
func Test(rw ReadWriter) {
rw.Read()
rw.Write()
}
func main() {
var f *File
var b interface{}
b = f
//Test(&f)
v, ok := b.(ReadWriter)
fmt.Println(v, ok)
}
只有当有两个或两个以上的具体类型必须以相同的方式进行处理时才需要定义接口。不要为了接口而写接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。