|
@@ -0,0 +1,110 @@
|
|
|
|
|
+# 10.7 类型的String()方法和格式化描述符
|
|
|
|
|
+
|
|
|
|
|
+当定义一个了有很多方法的类型时,十之八九你会使用String()方法来定制类型的字符串形式的输出,换句话说:一种可阅读性和打印性的输出。如果类型定义了String()方法,它会被用在fmt.Printf()中生成默认的输出:等同于使用格式化描述符%v产生的输出。还有fmt.Print()和fmt.Println()也会自动使用String()方法。
|
|
|
|
|
+
|
|
|
|
|
+我们使用10.4中程序的类型来进行测试:
|
|
|
|
|
+
|
|
|
|
|
+Listing 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", s)
|
|
|
|
|
+}
|
|
|
|
|
+t. String()
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**练习**:
|
|
|
|
|
+
|
|
|
|
|
+练习 10.12:type_string.go
|
|
|
|
|
+
|
|
|
|
|
+给定结构体类型T:
|
|
|
|
|
+
|
|
|
|
|
+```go
|
|
|
|
|
+type T struct {
|
|
|
|
|
+ a int
|
|
|
|
|
+ b float32
|
|
|
|
|
+ c string
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+值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 方法](10.6.md)
|
|
|
|
|
+- 下一节:[10.8 垃圾回收和SetFinalizer](10.8.md)
|