Go 语言笔记:接口
接口
接口是对对象行为的抽象和概括,Go 语言的接口类型是延迟绑定,接口只负责定义对象应该做什么,具体实现由对象本身确定。当一个类型定义了接口中的所有方法,我们认为它实现了该接口。
接口的定义
go
type interfaceName interface {
method()
}
接口的实现
go
package main
import "fmt"
// 定义 Animal 接口
type Animal interface {
Eat()
Play()
}
// 定义 Dog 结构体
type Dog struct {
name string
}
// 实现 Eat 方法
func (dog Dog) Eat() {
fmt.Printf("%s is eating bone.\n", dog.name)
}
// 实现 Play 方法
func (dog Dog) Play() {
fmt.Printf("%s is palying ball.\n", dog.name)
}
// 测试
func main() {
dog := Dog{name: "Tom"}
var tom Animal = &dog
tom.Eat()
tom.Play()
}
首先我们定义了 Animal 接口,其中有两个方法 Eat 和 Play,接着我们定义了 Dog 结构体且实现了 Animal 的两个方法。当我们注释掉其中一个方法的实现的时候,代码编译会报错。
go
// 我们将Play方法注释掉
//func (dog *Dog) Play() {
// fmt.Printf("%s is palying ball.", dog.name)
//}
// 该行编译报错
var tom Animal = &dog
编译错误信息如下
go
cannot use '&dog' (type *Dog) as type Animal in assignment
Type does not implement 'Animal' as some methods are missing: Play()
接口的多态
上面的示例,我们定义了结构体 Dog 实现了接口,我们可以再定义一个结构体 Cat 来实现接口,然后定义了 eat 方法,可以传入类型为 Animal 的参数
go
package main
import "fmt"
// 定义 Animal 接口
type Animal interface {
Eat()
Play()
}
// 定义 Dog 结构体
type Dog struct {
name string
}
// 实现 Eat 方法
func (dog Dog) Eat() {
fmt.Printf("%s is eating bone.\n", dog.name)
}
// 实现 Play 方法
func (dog Dog) Play() {
fmt.Printf("%s is palying ball.\n", dog.name)
}
// 定义 Cat 结构体
type Cat struct {
name string
}
// 实现 Eat 方法
func (cat Cat) Eat() {
fmt.Printf("%s is eating a mouse.\n", cat.name)
}
// 实现 Play 方法
func (cat Cat) Play() {
fmt.Printf("%s is palying a mouse.\n", cat.name)
}
// 定义函数,入参类型是 **Animal**
func eat(animal Animal) {
animal.Eat()
}
// 测试
func main() {
dog := Dog{name: "Tom"}
cat := Cat{name: "Jerry"}
eat(dog)
eat(cat)
}
实现接口的方式
方式一:值类型实现
go
// 定义 Animal 接口
type Animal interface {
Eat()
Play()
}
// 定义 Dog 结构体
type Dog struct {
name string
}
// 值类型实现 Eat 方法
func (dog Dog) Eat() {
fmt.Printf("%s is eating bone.\n", dog.name)
}
// 值类型实现 Play 方法
func (dog Dog) Play() {
fmt.Printf("%s is palying ball.\n", dog.name)
}
func main() {
dog := Dog{name: "Tom"}
eat(dog)
}
方式二:指针类型实现
go
// 定义 Animal 接口
type Animal interface {
Eat()
Play()
}
// 定义 Dog 结构体
type Dog struct {
name string
}
// 指针类型实现 Eat 方法
func (dog *Dog) Eat() {
fmt.Printf("%s is eating bone.\n", dog.name)
}
// 指针类型实现 Play 方法
func (dog *Dog) Play() {
fmt.Printf("%s is palying ball.\n", dog.name)
}
func main() {
dog := &Dog{name: "Tom"}
eat(dog)
}
方式三:封装函数
go
func getAnimal() Animal {
return Cat{name: "Jerry"}
}
func main() {
cat := getAnimal()
eat(cat)
}
空接口
空接口即没有定义任何方法的接口,是一种特殊形式的接口,可以说任何类型都至少实现了空接口,空接口表示为 interface{}。
go
func Print(v interface{}) {
fmt.Printf("%T: %v\n", v, v)
}
func main() {
Print(1) // int: 1
Print("Hello, Lemon!") // string: Hello, Lemon!
}
通过上面的示例,可以看出,接口有两个属性,一个类型,一个值,空接口两个参数都是 nil
go
func main() {
var param interface{}
fmt.Printf("Type: %T, Value: %v", param, param) // Type: <nil>, Value: <nil>
}
空接口的妙用
- 声明一个空接口实例 interface{},该实例能承载任何类型的值
go
func main() {
var param interface{}
param = 10
fmt.Printf("Type: %T, Value: %v", param, param) // Type: int, Value: 10
param = "Hi, Lemon."
fmt.Printf("Type: %T, Value: %v", param, param) // Type: string, Value: Hi, Lemon.
}
- 定义一个接受任何类型的数组、切片、Map、结构体
go
func main() {
x := make([]interface{}, 3)
x[0] = "Hi, Lemon."
x[1] = 10
x[2] = []int{0, 1, 2}
for _, value := range x {
fmt.Println(value)
}
}
空接口的使用禁忌
- 空接口对象不能赋值给固定类型的对象
go
func main() {
var age = 10
var param interface{} = age
// 编译错误:cannot use 'param' (type interface{}) as string in assignment
var str string = param
}
- 当空接口承载数组或切片后,该对象无法再进行切片
go
func main() {
var slice = []int{0, 1, 2}
var param interface{} = slice
// 编译错误:cannot slice param (type interface{})
var newSlice = param[1:2]
fmt.Println(newSlice)
}
接口类型断言
类型断言用于提取接口的底层值,可以使用 interface.(Type) 获取接口的底层值,其中接口 interface 的具体类型是 Type
go
func printTypeValue(itf interface{}) {
switch itf.(type) {
case int:
fmt.Printf("Type: int, Value: %d\n", itf.(int))
case string:
fmt.Printf("Type: string, Value: %s\n", itf.(string))
case Cat:
fmt.Printf("Type: Cat, Value: %s\n", itf.(Cat))
case Dog:
fmt.Printf("Type: Dog, Value: %s\n", itf.(Dog))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
age := 10
name := "Lemon"
cat := Cat{name: "Jerry"}
dog := Dog{name: "Tom"}
printTypeValue(age) // Type: int, Value: 10
printTypeValue(name) // Type: string, Value: Lemon
printTypeValue(cat) // Type: Cat, Value: {Jerry}
printTypeValue(dog) // Type: Dog, Value: {Tom}
}
多接口实现
go
// 定义 Animal 接口
type Animal interface {
Eat()
Play()
}
// 定义 Performance 接口
type Performance interface {
Show()
}
// 定义 Cat 结构体
type Cat struct {
name string
}
// 实现 Eat 方法
func (cat Cat) Eat() {
fmt.Printf("%s is eating a mouse.\n", cat.name)
}
// 实现 Play 方法
func (cat Cat) Play() {
fmt.Printf("%s is palying a mouse.\n", cat.name)
}
// 实现 Show 方法
func (cat Cat) Show() {
fmt.Printf("%s is showing.\n", cat.name)
}
此时 Cat 实现了多个接口,那么进行类型断言的时候到底以哪个为准呢?答案是:哪个case语句在前就认定是哪个接口类型
go
func printTypeValue(itf interface{}) {
switch itf.(type) {
case Performance:
fmt.Printf("Type: Performance, Value: %s\n", itf.(Performance))
case Animal:
fmt.Printf("Type: Animal, Value: %s\n", itf.(Animal))
default:
fmt.Printf("Unknown type\n")
}
}
func main() {
cat := Cat{name: "Jerry"}
printTypeValue(cat) // Type: Performance, Value: {Jerry}
}
接口嵌套
一个接口中可以包含其他的接口,称之为接口嵌套。我们可以在 io 包中看到: ReadWriter 接口中嵌套了 Reader 和 Writer 两个接口
go
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
type ReadWriter interface {
Reader
Writer
}