面试题答案
一键面试- 代码实现
package main
import (
"fmt"
"math"
)
// Shape接口
type Shape interface {
Area() float64
}
// Circle结构体,使用值接收者实现Area方法
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
// Rectangle结构体,使用指针接收者实现Area方法
type Rectangle struct {
Width float64
Height float64
}
func (r *Rectangle) Area() float64 {
return r.Width * r.Height
}
- 行为差异及原因分析
- 值接收者:
- 情况:当使用值接收者实现接口方法时,比如
Circle
结构体的Area
方法。如果对Circle
类型的变量进行复制,新的变量仍然可以调用Area
方法,并且计算的是其自身的面积。 - 原因:值接收者在方法调用时,实际上是对传入值的副本进行操作。所以即使原始值改变,副本调用方法的结果不受影响。例如:
- 情况:当使用值接收者实现接口方法时,比如
- 值接收者:
c1 := Circle{Radius: 5}
c2 := c1
c1.Radius = 10
fmt.Println(c1.Area()) // 输出100π
fmt.Println(c2.Area()) // 输出25π
- 指针接收者:
- 情况:当使用指针接收者实现接口方法时,比如
Rectangle
结构体的Area
方法。如果对Rectangle
类型的指针进行复制,两个指针指向同一个底层数据,任何一个指针修改数据,另一个指针调用Area
方法时结果会改变。同时,如果使用非指针类型的Rectangle
变量调用Area
方法,Go语言会自动进行隐式转换(前提是变量可寻址)。但如果是不可寻址的值(如临时变量),则不能调用指针接收者的方法。 - 原因:指针接收者在方法调用时,操作的是指针指向的底层数据。例如:
- 情况:当使用指针接收者实现接口方法时,比如
r1 := &Rectangle{Width: 5, Height: 10}
r2 := r1
r1.Width = 15
fmt.Println(r1.Area()) // 输出150
fmt.Println(r2.Area()) // 输出150
// 不可寻址值调用指针接收者方法会报错
// Rectangle{Width: 5, Height: 10}.Area() // 编译错误
总结来说,值接收者适合实现不需要修改结构体内部状态的方法,并且调用者对副本操作不影响原数据;指针接收者适合实现需要修改结构体内部状态的方法,或者希望在不同变量间共享底层数据的场景。同时要注意指针接收者方法调用时对可寻址性的要求。