方法与接口
翻译整理自 A Tour of Go - Methods and interfaces 与 Effective Go - Interfaces and other types。
1. 方法定义
Go 通过接收者(receiver) 为类型绑定方法:
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}调用:
r := Rectangle{Width: 3, Height: 4}
fmt.Println(r.Area()) // 122. 指针接收者 vs 值接收者
// 值接收者:方法操作的是副本
func (r Rectangle) Scale(factor float64) Rectangle {
return Rectangle{r.Width * factor, r.Height * factor}
}
// 指针接收者:方法可以修改原值
func (r *Rectangle) ScaleMut(factor float64) {
r.Width *= factor
r.Height *= factor
}选择建议:
- 需要修改接收者状态:用指针
- 结构体较大,避免每次拷贝:用指针
- 一致性:一个类型的方法建议统一选其一
- 小的不可变值类型(如
time.Time):可用值
3. 方法可以定义在任意包内类型
只能为当前包内定义的类型定义方法。可以通过别名绕开:
type MyString string
func (s MyString) Shout() MyString {
return MyString(strings.ToUpper(string(s)) + "!")
}4. 接口
接口是方法签名的集合。类型只要实现了接口中所有方法,即自动满足该接口(鸭子类型,无需显式声明):
type Shape interface {
Area() float64
Perimeter() float64
}任何实现了 Area() 和 Perimeter() 的类型都满足 Shape:
func Describe(s Shape) {
fmt.Println(s.Area(), s.Perimeter())
}
Describe(Rectangle{3, 4}) // 12 14空接口与 any
interface{}(Go 1.18+ 起别名为 any)接受任意类型:
func Print(v any) {
fmt.Println(v)
}5. 类型断言与 type switch
var i interface{} = "hello"
s, ok := i.(string) // 带检查
if ok {
fmt.Println(s)
}
// type switch
switch v := i.(type) {
case string:
fmt.Println("string:", v)
case int:
fmt.Println("int:", v)
default:
fmt.Printf("%T\n", v)
}6. Stringer 接口
实现 String() 方法的类型会被 fmt 自动调用:
type Person struct{ Name string; Age int }
func (p Person) String() string {
return fmt.Sprintf("%s(%d)", p.Name, p.Age)
}
fmt.Println(Person{"Alice", 30}) // Alice(30)7. error 与 error 接口
error 是内置接口:
type error interface {
Error() string
}自定义错误:
type MyError struct {
Code int
Msg string
}
func (e *MyError) Error() string {
return fmt.Sprintf("code=%d msg=%s", e.Code, e.Msg)
}8. 接口组合
接口可嵌入其他接口:
type ReadWriter interface {
io.Reader
io.Writer
}io.ReadWriter 即要求同时满足 Reader 和 Writer。
9. 接口设计原则
Accept interfaces, return structs. 函数参数尽量用接口以提升可替换性,返回值用具体类型便于调用方使用。
小结
Go 的方法与接口以隐式实现为核心,避免了 Java 那种显式 implements,让代码解耦更自然。下一章学习 Go 的杀手锏:并发。