二、接口(Interface)
接口定义了一组方法签名,它只描述“能做什么”,而不关心具体的类型。在 Go 中,接口是隐式实现的:如果一个类型实现了接口中定义的所有方法,那么它就自动实现了该接口,无需显式声明。
1. 接口的定义
使用 type 和 interface 关键字定义接口:
type Speaker interface {
Speak() string
}
接口 Speaker 要求任何实现它的类型必须有一个 Speak() string 方法。
2. 接口的实现
假设我们有两个结构体:Person 和 Dog,它们都实现了 Speak 方法:
type Person struct {
Name string
}
func (p Person) Speak() string {
return "你好,我是 " + p.Name
}
type Dog struct {
Breed string
}
func (d Dog) Speak() string {
return "汪汪!我是 " + d.Breed
}
现在,Person 和 Dog 都隐式地实现了 Speaker 接口。
3. 使用接口实现多态
我们可以声明一个接口类型的变量,它可以保存任何实现了该接口的值:
func main() {
var s Speaker
s = Person{Name: "小明"}
fmt.Println(s.Speak()) // 输出:你好,我是 小明
s = Dog{Breed: "金毛"}
fmt.Println(s.Speak()) // 输出:汪汪!我是 金毛
}
同一个接口变量,根据其底层具体类型的不同,表现出不同的行为,这就是多态。
接口还可以作为函数参数,接受任何实现了该接口的类型:
func MakeSound(s Speaker) {
fmt.Println(s.Speak())
}
func main() {
MakeSound(Person{Name: "小红"})
MakeSound(Dog{Breed: "柯基"})
}
4. 空接口 interface{} 和 any
空接口 interface{} 没有定义任何方法,因此所有类型都实现了空接口。它可以用来表示任意类型的值。从 Go 1.18 开始,any 是 interface{} 的别名,含义相同。
var i interface{}
i = 42
i = "hello"
i = Person{Name: "测试"}
// 使用 any
var a any = 3.14
空接口经常用于需要处理未知类型的情况,但使用时需要配合类型断言来恢复具体类型。
5. 类型断言
类型断言用于提取接口值中的具体类型。语法:value, ok := 接口变量.(具体类型)。ok 表示断言是否成功,避免 panic。
var s Speaker = Person{Name: "小明"}
// 类型断言
p, ok := s.(Person)
if ok {
fmt.Println("s 是 Person 类型,姓名:", p.Name)
} else {
fmt.Println("s 不是 Person 类型")
}
// 直接断言,不检查会 panic
// d := s.(Dog) // panic
也可以使用 type switch 进行多种类型判断:
switch v := s.(type) {
case Person:
fmt.Println("Person:", v.Name)
case Dog:
fmt.Println("Dog:", v.Breed)
default:
fmt.Println("未知类型")
}
6. 接口组合
接口可以通过嵌套其他接口来创建新接口,类似于结构体嵌套。例如:
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
}
任何类型只要同时实现了 Reader 和 Writer 接口的方法,就自动实现了 ReadWriter。
7. 常用接口示例
Go 标准库中有许多有用的接口,例如 fmt.Stringer:
type Stringer interface {
String() string
}
任何实现了 String() string 方法的类型,在使用 fmt.Print 等函数时会自动调用该方法获取字符串表示。
func (p Person) String() string {
return fmt.Sprintf("Person(Name=%s, Age=%d)", p.Name, p.Age)
}
func main() {
p := Person{Name: "小明", Age: 20}
fmt.Println(p) // 输出:Person(Name=小明, Age=20)
}
三、总结
- 结构体:用于定义复杂数据类型,支持嵌套和方法,是数据组织的基石。
- 接口:定义行为规范,支持隐式实现,是实现多态和解耦的关键工具。
- 结构体和接口结合使用,可以构建出灵活、可扩展的程序。