Golang中实现Stringer接口时遇到的困惑
Golang中实现Stringer接口时遇到的困惑 你好, 我是Go语言的新手,正在学习官方的Go语言之旅。我对这个教程中的内容有一些疑问。 Go语言之旅的完整代码在页面底部。 当我将
func (p Person) String() string
的接收者改为
func (p *Person) String() string
时,它不会打印出String方法返回的字符串。
看起来在fmt.Println()调用中没有调用String方法。
非常感谢任何帮助!
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z)
}
更多关于Golang中实现Stringer接口时遇到的困惑的实战教程也可以访问 https://www.itying.com/category-94-b0.html
当你将 a 和 z 传递给 fmt.Println 时,Go 首先会将它们转换为 interface{}(请记住这一点)。然后通过类型开关,它会发现你传递的值不是一个 fmt.Stringer,因为你是在 *Person 上定义了 String(),而不是在 Person 上,这两者是有区别的。
fmt.Println 内部应该执行类似这样的操作:
switch v := v.(type) {
case string:
os.Stdout.WriteString(v)
case fmt.Stringer:
os.Stdout.WriteString(v.String())
// ...
}
为了验证这一点,将你的代码行 fmt.Println(a, z) 改为 fmt.Println(&a, &z),它就能正常工作了。
在Go语言中,fmt.Println等格式化函数会根据接收者类型决定是否调用String()方法。当使用指针接收者时,需要确保调用的是指针类型的方法。
问题在于:当使用值接收者(p Person)时,fmt.Println可以隐式获取值的String()方法。但改为指针接收者(p *Person)后,对于值类型的变量,Go不会自动获取其指针方法。
以下是示例代码,展示两种解决方案:
方案1:使用指针变量
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p *Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := &Person{"Arthur Dent", 42} // 使用指针
z := &Person{"Zaphod Beeblebrox", 9001} // 使用指针
fmt.Println(a, z) // 现在会调用String()方法
}
方案2:同时实现值和指针接收者
package main
import "fmt"
type Person struct {
Name string
Age int
}
// 指针接收者
func (p *Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
// 值接收者(调用指针版本)
func (p Person) String() string {
return (&p).String()
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
fmt.Println(a, z) // 两种方式都能工作
}
方案3:使用接口类型断言
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p *Person) String() string {
return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}
func main() {
a := Person{"Arthur Dent", 42}
z := Person{"Zaphod Beeblebrox", 9001}
// 显式转换为fmt.Stringer接口
fmt.Println(fmt.Stringer(&a), fmt.Stringer(&z))
}
根本原因是Go的方法集规则:
- 类型
T的方法集包含所有接收者为T的方法 - 类型
*T的方法集包含所有接收者为T和*T的方法
当String()使用指针接收者时,只有*Person类型实现了fmt.Stringer接口,而Person类型没有实现。

