type User struct {
name string
age int
}
func (u *User) String() string {
return fmt.Sprintf("name = %s, age = %d", u.name, u.age)
}
func main() {
u1 := User{
name: "old",
age: 12,
}
fmt.Println(u1)
}
在上面的代码中
fmt.Println
的接收器类型为:any(空接口)。
假设:
指针接收器方法不能在值持有接口上调用,因为方法内部所做的任何更改都不会反映在原始结构中,而只会反映在接口持有的副本上。
但是这段代码可以正常工作,没有任何编译/运行时错误,结果如下。
结果:
{old 12}
看起来只是在运行时跳过了调用,没有任何错误。 这是预期的行为吗?
String
方法不会被调用,因为来自Effective Go:方法、指针与值...的这条规则
...值方法可以在指针和值上调用,但指针方法只能在指针上调用...出现此规则是因为指针方法可以修改接收者;对值调用它们将导致该方法接收该值的副本,因此任何修改都将被丢弃。因此,该语言不允许出现这种错误。
func (u *User) String() string
是一种指针方法。 u1
是用户值。 fmt.Println(u1)
传递一个 User 值,因此它不会使用您的 String 方法。
fmt.Println(&u1)
确实使用了您的 String 方法,您传递了一个指针,因此它可以使用指针方法。
由于
String
并不打算就地改变User,所以它应该是一个值方法。您应该将字符串更改为 func (u User) String() string
。
也许令人困惑,
fmt.Println(u1.String())
会起作用。这是因为为了您的方便,Go 已经悄悄地将其更改为 fmt.Println(&u1.String())
。
不过,有一个方便的例外。当值是可寻址的时,语言会通过自动插入地址运算符来处理对值调用指针方法的常见情况。在我们的示例中,变量 b 是可寻址的,因此我们可以仅使用 b.Write 调用其 Write 方法。编译器会将其重写为 (&b)。为我们编写。