10.7 类型的 String() 方法和格式化描述符

当定义了一个有很多方法的类型时,十之八九你会使用 String() 方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了 String() 方法,它会被用在 fmt.Printf() 中生成默认的输出:等同于使用格式化描述符 %v 产生的输出。还有 fmt.Print()fmt.Println() 也会自动使用 String() 方法。

我们使用第 10.4 节中程序的类型来进行测试:

示例 10.22 method_string.go

  1. package main
  2. import (
  3. "fmt"
  4. "strconv"
  5. )
  6. type TwoInts struct {
  7. a int
  8. b int
  9. }
  10. func main() {
  11. two1 := new(TwoInts)
  12. two1.a = 12
  13. two1.b = 10
  14. fmt.Printf("two1 is: %v\n", two1)
  15. fmt.Println("two1 is:", two1)
  16. fmt.Printf("two1 is: %T\n", two1)
  17. fmt.Printf("two1 is: %#v\n", two1)
  18. }
  19. func (tn *TwoInts) String() string {
  20. return "(" + strconv.Itoa(tn.a) + "/" + strconv.Itoa(tn.b) + ")"
  21. }

输出:

  1. two1 is: (12/10)
  2. two1 is: (12/10)
  3. two1 is: *main.TwoInts
  4. two1 is: &main.TwoInts{a:12, b:10}

当你广泛使用一个自定义类型时,最好为它定义 String()方法。从上面的例子也可以看到,格式化描述符 %T 会给出类型的完全规格,%#v 会给出实例的完整输出,包括它的字段(在程序自动生成 Go 代码时也很有用)。

备注

不要在 String() 方法里面调用涉及 String() 方法的方法,它会导致意料之外的错误,比如下面的例子,它导致了一个无限递归调用(TT.String() 调用 fmt.Sprintf,而 fmt.Sprintf 又会反过来调用 TT.String()),很快就会导致内存溢出:

  1. type TT float64
  2. func (t TT) String() string {
  3. return fmt.Sprintf("%v", t)
  4. }
  5. t.String()

练习 10.12 type_string.go

给定结构体类型 T:

  1. type T struct {
  2. a int
  3. b float32
  4. c string
  5. }

t: t := &T{7, -2.35, "abc\tdef"}。给 T 定义 String(),使得 fmt.Printf("%v\n", t) 输出:7 / -2.350000 / "abc\tdef"

练习 10.13 celsius.go

float64 定义一个别名类型 Celsius,并给它定义 String(),它输出一个十进制数和 °C 表示的温度值。

练习 10.14 days.go

int 定义一个别名类型 Day,定义一个字符串数组它包含一周七天的名字,为类型 Day 定义 String() 方法,它输出星期几的名字。使用 iota 定义一个枚举常量用于表示一周的中每天(MO、TU…)。

练习 10.15 timezones.go

int 定义别名类型 TZ,定义一些常量表示时区,比如 UTC,定义一个 map,它将时区的缩写映射为它的全称,比如:UTC -> "Universal Greenwich time"。为类型 TZ 定义 String() 方法,它输出时区的全称。

练习 10.16 stack_arr.go / stack_struct.go

实现栈 (stack) 数据结构:

10.7 类型的 String() 方法和格式化描述符 - 图1

它的格子包含数据,比如整数 ijkl 等等,格子从底部(索引 0)至顶部(索引 n)来索引。这个例子中假定 n = 3,那么一共有 4 个格子。

一个新栈中所有格子的值都是 0

将一个新值放到栈的最顶部一个空(包括零)的格子中,这叫做 push。

获取栈的最顶部一个非空(非零)的格子的值,这叫做 pop。 现在可以理解为什么栈是一个后进先出 (LIFO) 的结构了吧。

为栈定义一个 Stack 类型,并为它定义 PushPop 方法,再为它定义 String() 方法(用于调试)输出栈的内容,比如:[0:i] [1:j] [2:k] [3:l]

1)stack_arr.go:使用长度为 4 的 int 数组作为底层数据结构。

2) stack_struct.go:使用包含一个索引和一个 int 数组的结构体作为底层数据结构,索引表示第一个空闲的位置。

3)使用常量 LIMIT 代替上面表示元素个数的 4 重新实现上面的 1)和 2),使它们更具有一般性。

链接