golang 鸭子类型(Duck Typing)
1. 基础示例:接口的隐式实现
代码
package main
import "fmt"
// 定义接口 Duck,要求实现 Quack() 方法
type Duck interface {
Quack() string // 鸭子必须会“嘎嘎”叫
}
// 实现 Duck 接口的结构体:RealDuck(真实鸭子)
type RealDuck struct{}
func (r RealDuck) Quack() string {
return "Quack! Quack!" // 真实鸭子的叫声
}
// 实现 Duck 接口的结构体:ToyDuck(玩具鸭子)
type ToyDuck struct{}
func (t ToyDuck) Quack() string {
return "Squeak! Squeak!" // 玩具鸭子的叫声
}
// 接收 Duck 接口的函数
func MakeDuckQuack(d Duck) {
fmt.Println(d.Quack()) // 调用接口的 Quack 方法
}
func main() {
// 实例化不同类型的鸭子
real := RealDuck{}
toy := ToyDuck{}
// 传递给函数时,无需关心具体类型,只要实现接口即可
MakeDuckQuack(real) // 输出:Quack! Quack!
MakeDuckQuack(toy) // 输出:Squeak! Squeak!
}
关键点
- 接口隐式实现:
RealDuck 和 ToyDuck 没有显式声明实现 Duck 接口,但通过实现 Quack() 方法,隐式满足接口要求。
- 多态性:
MakeDuckQuack 函数接受任何实现了 Duck 接口的类型,无需修改代码即可适配新类型。
2. 鸭子类型的核心特性:动态行为匹配
代码
package main
import "fmt"
// 定义接口:Bird,要求实现 Fly() 和 Sing() 方法
type Bird interface {
Fly() string
Sing() string
}
// 结构体:Eagle(老鹰)
type Eagle struct{}
func (e Eagle) Fly() string {
return "Flying high in the sky!"
}
func (e Eagle) Sing() string {
return "Screech! Screech!" // 老鹰的叫声
}
// 结构体:Parrot(鹦鹉)
type Parrot struct{}
func (p Parrot) Fly() string {
return "Flapping wings!"
}
func (p Parrot) Sing() string {
return "Hello! I'm a parrot!" // 鹦鹉模仿人类说话
}
func main() {
// 将不同鸟类传递给函数
makeBirdFlyAndSing := func(b Bird) {
fmt.Println("Fly:", b.Fly())
fmt.Println("Sing:", b.Sing())
}
makeBirdFlyAndSing(Eagle{}) // 输出:Fly... 和 Screech...
makeBirdFlyAndSing(Parrot{}) // 输出:Fly... 和 Hello...
}
关键点
- 动态行为匹配:只要类型实现了接口的所有方法,无论类型是
Eagle 还是 Parrot,都可以被 Bird 接口接受。
- 无需显式关联:类型无需声明“我是 Bird”,只需实现方法即可。
3. 接口嵌套与组合:复杂场景
代码
package main
import "fmt"
// 定义基础接口
type Animal interface {
Move() string
}
type Pet interface {
Name() string
}
// 组合接口:定义一个更复杂的接口
type PetAnimal interface {
Animal // 继承 Animal 接口
Pet // 继承 Pet 接口
Play() // 新增 Play 方法
}
// 实现 PetAnimal 接口的结构体:Dog
type Dog struct{}
func (d Dog) Move() string {
return "Running on four legs"
}
func (d Dog) Name() string {
return "Buddy"
}
func (d Dog) Play() {
fmt.Println("Chasing the ball!")
}
func main() {
buddy := Dog{}
// 验证接口实现
var pa PetAnimal = buddy // 隐式实现 PetAnimal 接口
fmt.Println(pa.Move()) // 输出:Running on four legs
fmt.Println(pa.Name()) // 输出:Buddy
pa.Play() // 输出:Chasing the ball!
}
关键点
- 接口嵌套:
PetAnimal 继承了 Animal 和 Pet 接口,并新增 Play() 方法。
- 组合优于继承:Go 通过接口嵌套实现功能组合,避免传统 OOP 的继承层级问题。
4. 鸭子类型 vs 静态类型检查
代码示例
package main
import "fmt"
type Swimmer interface {
Swim() string
}
type Fish struct{}
func (f Fish) Swim() string {
return "Swimming in water!"
}
func main() {
var s Swimmer = Fish{} // 鱼实现了 Swimmer 接口
fmt.Println(s.Swim()) // 输出:Swimming in water!
// 错误示例:类型未实现接口
// type Bird struct{} // 没有实现 Swim() 方法
// var b Swimmer = Bird{} // 编译错误:Bird 不实现 Swim()
}
关键点
- 静态类型检查:Go 在编译时会严格检查类型是否实现接口的所有方法,未实现则报错(如
Bird 的例子)。
- 鸭子类型的边界:Go 的接口是“静态鸭子类型”,即行为匹配需在编译时确定,而非运行时。