Golang中Stringer接口的使用问题求助

Golang中Stringer接口的使用问题求助

//convert.go
package convert

type Liters float64
type Milliliters float64

func (l *Liters) ToMilliliters() Milliliters{
    return Milliliters(*l * 1000)
}

func (m *Milliliters) ToLiters() Liters{
    return Liters(*m / 1000)
}

func (m *Milliliters) String() string {
    return fmt.Sprintf("%0.2f ml", *m)
}

func (l *Liters) String() string {
    return fmt.Sprintf("%0.2f l", *l)
}

//main.go
package main

import (
    "fmt"
    "convert"
)

func main() {
    vol := Liters(2)
    fmt.Println(vol.ToMilliliters())  //输出 2000, 但我想要 2000.00 ml
}

注意:当方法使用值接收器时,我得到了正确的输出,即当我将 func (l *Liters) String() string 替换为 func (l Liters) String() string 时。


更多关于Golang中Stringer接口的使用问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

你好!感谢回复。我确实没太理解这一点。

在我的例子中,LitersMilliliters 是已定义类型。但是,我仍然可以在 vol(它是 Liters 类型)上调用具有指针接收者的 ToMilliliters() 方法,而无需使用指针,不是吗?

更多关于Golang中Stringer接口的使用问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 @void5253

只有指针类型才能访问拥有指针接收器的方法。关于此点的详细信息,请参阅语言规范中的方法集合,特别是:

  • 一个定义类型 T 的方法集合包含所有以接收器类型 T 声明的方法
  • 一个指向定义类型 T 的指针(其中 T 既不是指针也不是接口)的方法集合,是所有以接收器 *TT 声明的方法的集合。

vol 不是指针类型,因此它的方法集合不包含在指针接收器上定义的 String() 方法。因此,应用的是 float64(2) 的标准字符串表示形式。

因此,一个同时对 Milliliters*Milliliters 可用的 String() 方法需要一个值接收器。

你在这里提出了一个很好的观点。这段 Playground 代码 表明,String() 方法在 Milliliters 类型的变量上可以正常工作,但在 Milliliters 类型的返回值上却不行。

	mvol := vol.ToMilliliters()
	fmt.Println(mvol.String())                // 2000.00 ml
	fmt.Println(vol.ToMilliliters().String()) // error: cannot call pointer method String on Milliliters

所以,我最初的分析过于草率了。

问题在于变量是可寻址的,而返回值不是。

. 操作符很智能,它既适用于值类型也适用于指针类型。对于指针,它会自动解引用。

但这对于不可寻址的返回值不起作用。

这就是一门语言包含便利特性时会发生的情况。这些特性可能有所帮助,但也可能引入像这样的意外行为。

另请参阅:go - “cannot take the address of” and “cannot call pointer method on” - Stack Overflow

编辑补充:

你的特定代码有一个非常直接的解决方案:使用值接收器。你的方法不会以任何方式修改接收器;因此,使用值接收器是完全没问题的。

你的问题是因为 String() 方法使用了指针接收器,而 fmt.Println 在调用 String() 方法时,需要能够获取到该方法的接收器。当你调用 vol.ToMilliliters() 时,返回的是 Milliliters 类型的值,而不是指针,因此无法调用指针接收器的 String() 方法。

以下是修改后的代码示例:

// convert.go
package convert

import "fmt"

type Liters float64
type Milliliters float64

func (l Liters) ToMilliliters() Milliliters {
    return Milliliters(l * 1000)
}

func (m Milliliters) ToLiters() Liters {
    return Liters(m / 1000)
}

func (m Milliliters) String() string {
    return fmt.Sprintf("%0.2f ml", m)
}

func (l Liters) String() string {
    return fmt.Sprintf("%0.2f l", l)
}

// main.go
package main

import (
    "fmt"
    "convert"
)

func main() {
    vol := convert.Liters(2)
    fmt.Println(vol.ToMilliliters()) // 现在输出 2000.00 ml
}

关键修改:

  1. 将所有方法改为值接收器(包括 String() 方法)
  2. main.go 中明确使用 convert. 前缀(假设包名正确)

这样修改后,fmt.Println 就能正确调用 Milliliters 类型的 String() 方法,输出格式化的字符串了。

回到顶部