mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-11 23:08:34 +08:00
* 翻译修正 A new value is put in the highest cell which is empty (contains 0), on top: this is called push. * Update 10.7.md 原文中的which此处可省略掉,译文更通顺
113 lines
4.1 KiB
Markdown
113 lines
4.1 KiB
Markdown
# 10.7 类型的 String() 方法和格式化描述符
|
||
|
||
当定义了一个有很多方法的类型时,十之八九你会使用 `String()` 方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了 `String()` 方法,它会被用在 `fmt.Printf()` 中生成默认的输出:等同于使用格式化描述符 `%v` 产生的输出。还有 `fmt.Print()` 和 `fmt.Println()` 也会自动使用 `String()` 方法。
|
||
|
||
我们使用第 10.4 节中程序的类型来进行测试:
|
||
|
||
示例 10.22 method_string.go:
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"strconv"
|
||
)
|
||
|
||
type TwoInts struct {
|
||
a int
|
||
b int
|
||
}
|
||
|
||
func main() {
|
||
two1 := new(TwoInts)
|
||
two1.a = 12
|
||
two1.b = 10
|
||
fmt.Printf("two1 is: %v\n", two1)
|
||
fmt.Println("two1 is:", two1)
|
||
fmt.Printf("two1 is: %T\n", two1)
|
||
fmt.Printf("two1 is: %#v\n", two1)
|
||
}
|
||
|
||
func (tn *TwoInts) String() string {
|
||
return "(" + strconv.Itoa(tn.a) + "/" + strconv.Itoa(tn.b) + ")"
|
||
}
|
||
```
|
||
|
||
输出:
|
||
|
||
two1 is: (12/10)
|
||
two1 is: (12/10)
|
||
two1 is: *main.TwoInts
|
||
two1 is: &main.TwoInts{a:12, b:10}
|
||
|
||
当你广泛使用一个自定义类型时,最好为它定义 `String()`方法。从上面的例子也可以看到,格式化描述符 `%T` 会给出类型的完全规格,`%#v` 会给出实例的完整输出,包括它的字段(在程序自动生成 `Go` 代码时也很有用)。
|
||
|
||
**备注**
|
||
|
||
不要在 `String()` 方法里面调用涉及 `String()` 方法的方法,它会导致意料之外的错误,比如下面的例子,它导致了一个无限递归调用(`TT.String()` 调用 `fmt.Sprintf`,而 `fmt.Sprintf` 又会反过来调用 `TT.String()`...),很快就会导致内存溢出:
|
||
|
||
```go
|
||
type TT float64
|
||
|
||
func (t TT) String() string {
|
||
return fmt.Sprintf("%v", t)
|
||
}
|
||
t.String()
|
||
```
|
||
|
||
**练习 10.12** type_string.go
|
||
|
||
给定结构体类型 T:
|
||
|
||
```go
|
||
type T struct {
|
||
a int
|
||
b float32
|
||
c string
|
||
}
|
||
```
|
||
|
||
值 `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)数据结构:
|
||
|
||

|
||
|
||
它的格子包含数据,比如整数 i、j、k 和 l 等等,格子从底部(索引 0)至顶部(索引 n)来索引。这个例子中假定 `n=3`,那么一共有 4 个格子。
|
||
|
||
一个新栈中所有格子的值都是 0。
|
||
|
||
将一个新值放到栈的最顶部一个空(包括零)的格子中,这叫做`push`。
|
||
|
||
获取栈的最顶部一个非空(非零)的格子的值,这叫做`pop`。
|
||
现在可以理解为什么栈是一个后进先出(LIFO)的结构了吧。
|
||
|
||
为栈定义一个`Stack` 类型,并为它定义 `Push` 和 `Pop` 方法,再为它定义 `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),使它们更具有一般性。
|
||
|
||
## 链接
|
||
|
||
- [目录](directory.md)
|
||
- 上一节:[方法](10.6.md)
|
||
- 下一节:[垃圾回收和 SetFinalizer](10.8.md)
|