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

3 回复

非常感谢!我明白了。

更多关于Golang中实现Stringer接口时遇到的困惑的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


当你将 az 传递给 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类型没有实现。

回到顶部