面向对象:接口与继承
Go 语言没有 class,面向对象通过 struct + 方法 + 接口来实现。接口(interface)用于定义行为集合、实现多态;继承通过结构体嵌入(struct embedding)实现组合式复用。
接口(interface)
接口定义了一组方法签名,任何类型只要实现了这些方法,就自动实现了该接口,不需要显式声明。
隐式实现:Go 中没有 implements 关键字,只要方法集合匹配就认为实现了接口
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct {
Radius float64
}
// Circle 实现 Shape 接口
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
type Rectangle struct {
width, height float64
}
func (r Rectangle) Area() float64 {
return r.width * r.height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.width + r.height)
}
func main() {
// 接口变量可以存储任何实现了该接口的值
var s Shape = Circle{Radius: 5}
fmt.Println("Circle Area:", s.Area())
s = Rectangle{width: 10, height: 5}
fmt.Println("Rect Area:", s.Area())
}
接口的特性
| 特性 | 说明 |
|---|---|
| 隐式实现 | 无需显式声明,方法匹配即实现 |
| 接口变量 | 存储任意实现该接口的值,包含动态类型和动态值 |
| 零值 | 接口的零值是 nil |
| 空接口 | interface{},可以表示任何类型 |
package main
import "fmt"
func printValue(val interface{}) {
fmt.Printf("Value: %v, Type: %T\n", val, val)
}
func main() {
var i interface{}
fmt.Println(i == nil) // true,未初始化的接口零值为 nil
printValue(42) // Value: 42, Type: int
printValue("hello") // Value: hello, Type: string
printValue(3.14) // Value: 3.14, Type: float64
}
接口嵌套
接口可以通过嵌套组合出更复杂的行为。
package main
import "fmt"
type Reader interface {
Read() string
}
type Writer interface {
Write(data string)
}
type ReadWriter interface {
Reader
Writer
}
type File struct{}
func (f File) Read() string {
return "Reading data"
}
func (f File) Write(data string) {
fmt.Println("Writing data:", data)
}
func main() {
var rw ReadWriter = File{}
fmt.Println(rw.Read())
rw.Write("Hello, Go!")
}
类型断言简述
从接口中提取具体类型使用类型断言:value, ok := iface.(Type)。断言失败时 ok 为 false,不会 panic。
类型断言和 type switch 的详细用法见 25-assertion.md
继承:结构体嵌入
Go 没有继承,使用结构体嵌入来实现组合式复用。子结构体可以"继承"父结构体的字段和方法。
package main
import "fmt"
// 父结构体
type Animal struct {
Name string
}
func (a *Animal) Speak() {
fmt.Println(a.Name, "says hello!")
}
// 子结构体,嵌入 Animal
type Dog struct {
Animal
Breed string
}
func main() {
dog := Dog{
Animal: Animal{Name: "Buddy"},
Breed: "Golden Retriever",
}
dog.Speak() // 调用父结构体的方法
fmt.Println("Breed:", dog.Breed)
}
方法重写
子结构体可以定义同名方法覆盖父结构体的方法,仍然可以通过 父结构体名.方法 访问父类的方法。
package main
import "fmt"
type Vehicle struct {
Brand string
}
func (v *Vehicle) Start() {
fmt.Println(v.Brand, "started")
}
type Car struct {
Vehicle
Model string
}
// 重写 Start 方法
func (c *Car) Start() {
fmt.Println(c.Brand, c.Model, "car started")
}
func main() {
v := Vehicle{Brand: "Toyota"}
c := Car{Vehicle: Vehicle{Brand: "Honda"}, Model: "Civic"}
v.Start() // Toyota started
c.Start() // Honda Civic car started(调用重写后的方法)
c.Vehicle.Start() // Honda started(显式调用父类方法)
}
多态
通过接口实现类似继承的多态:不同的结构体实现同一个接口,赋给接口变量后统一调用。
package main
import "fmt"
type Speaker interface {
Speak()
}
type Animal struct {
Name string
}
func (a *Animal) Speak() {
fmt.Println(a.Name, "says hello!")
}
type Cat struct {
Animal
}
func (c *Cat) Speak() {
fmt.Println(c.Name, "says meow!")
}
func main() {
var speaker Speaker
speaker = &Animal{Name: "Buddy"}
speaker.Speak() // Buddy says hello!
speaker = &Cat{Animal: Animal{Name: "Mimi"}}
speaker.Speak() // Mimi says meow!(调用 Cat 自己的方法)
}
Go 与经典继承的区别
| 特性 | 经典继承 | Go 的方式 |
|---|---|---|
| 代码复用 | 通过继承 | 通过组合(嵌入结构体) |
| 多态 | 通过继承和方法重写 | 通过接口实现 |
| 关系 | "是一个"(is-a)关系 | "有一个"(has-a)或"实现了"关系 |
| 灵活性 | 继承关系固定 | 可以运行时组合 |
Go 通过组合避免了很多经典继承的问题(如脆弱的基类),同时提供了更大的灵活性