在 Go 语言中,接口的实现规则取决于方法的接收者类型(值接收者 (T) 还是 指针接收者 (*T))。具体规则如下:
1. 值接收者 (T) 实现接口
如果方法使用 值接收者(func (t T) Method()),那么:
-
T和*T都算实现了接口(Go 会自动解引用指针)。 -
赋值给接口时,可以用
T或*T。
示例
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
// 值接收者
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
var s Shape
// ✅ 可以用 Circle(值类型)
c1 := Circle{Radius: 5}
s = c1 // 正确
fmt.Println(s.Area())
// ✅ 也可以用 *Circle(指针类型)
c2 := &Circle{Radius: 10}
s = c2 // 也正确
fmt.Println(s.Area())
}
结论:值接收者的方法,T 和 *T 都算实现了接口。
2. 指针接收者 (*T) 实现接口
如果方法使用 指针接收者(func (t *T) Method()),那么:
-
只有
*T算实现了接口,T不算。 -
赋值给接口时,必须用
*T,不能用T(否则编译错误)。
示例
type Shape interface {
Area() float64
}
type Circle struct {
Radius float64
}
// 指针接收者
func (c *Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func main() {
var s Shape
// ❌ 不能用 Circle(值类型)
c1 := Circle{Radius: 5}
s = c1 // 编译错误:Circle does not implement Shape (Area method has pointer receiver)
// ✅ 必须用 *Circle(指针类型)
c2 := &Circle{Radius: 10}
s = c2 // 正确
fmt.Println(s.Area())
}
结论:指针接收者的方法,只有 *T 算实现了接口,T 不算。
3. 为什么 Go 这样设计?
-
值接收者
(T):- 方法不会修改原对象(因为是副本)。
- Go 允许
T和*T都实现接口,更灵活。
-
指针接收者
(*T):- 方法可能修改原对象(因为是引用)。
- 只有
*T能保证正确的行为,所以T不算实现接口。
4. 如何选择接收者类型?
| 情况 | 推荐接收者类型 |
|---|---|
| 方法不需要修改结构体 |
值接收者 (T)(更通用) |
| 方法需要修改结构体 | 指针接收者 (*T) |
| 结构体较大(避免复制开销) | 指针接收者 (*T) |
| 需要强制使用指针(如实现接口) | 指针接收者 (*T) |
5. 总结
| 方法接收者 | 实现接口的类型 | 赋值给接口的方式 |
|---|---|---|
(T) Method() |
T 和 *T
|
var s Shape = T{} 或 var s Shape = &T{}
|
(*T) Method() |
仅 *T |
var s Shape = &T{}(不能用 T{}) |
记住:
-
值接收者 → 更灵活,
T和*T都能用。 -
指针接收者 → 更严格,只有
*T能赋值给接口。
这样设计是为了保证代码的安全性和一致性。🚀