跳转到主要内容
模块 6 · 第 4 课(共 5)课程中的29/50~10 min
模块课程(4/5)

斯金格接口

fmt.Stringer 可能是所有接口中最常用的标准接口 Go 的标准库。是实力的完美展现 小接口。

定义

Go
type Stringer interface {
    String() string
}

单一签名。任何定义 String() string 的类型都满足 它。并且 fmt 识别此接口:当您将值传递给 fmt.Print*,如果它实现 Stringer,则为 String() 的结果 使用而不是默认表示。

例子

Go
type Point struct{ X, Y int }

func (p Point) String() string {
    return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}

fmt.Println(Point{1, 2})              // (1,2)
fmt.Printf("%v %s\n", Point{3, 4}, Point{5, 6})
// (3,4) (5,6)

在 %v、%s、Println 中以自定义格式打印相同的值, Sprint、log 等。所有这些都只需定义一种方法即可。

当它有意义时

  • 模拟域的类型(id、代码、坐标、枚举)。
  • 人类可读表示的错误(请参阅下一课的 error)。
  • 您经常记录并且需要固定格式的类型。

不适用于所有情况:如果类型很简单且默认表示形式 很好,不要添加噪音。

枚举类型:带有 iota 的常见模式

Go
type Status int

const (
    OK Status = iota
    WARN
    ERR
)

func (s Status) String() string {
    switch s {
    case OK:   return "OK"
    case WARN: return "WARN"
    case ERR:  return "ERR"
    default:   return "UNKNOWN"
    }
}

fmt.Println(OK, WARN, ERR)   // OK WARN ERR

如果没有 String(),fmt.Println(OK) 将打印 0。使用 String(), 它打印符号名称。 stringer 工具自动生成此模式。

注意无限递归

Go
type T int
func (t T) String() string {
    return fmt.Sprintf("%d", int(t))  // OK: cast to int breaks the recursion
}

指针接收器上的纵梁

如果 String() 有指针接收器,则只有 *T 满足 Stringer。 通过 fmt.Println(t) (一个值),Go 查看 T 的方法集, 找不到它,并使用默认格式。为了避免这种情况,值 receive 通常用于 String() (它是一个“视图”;它不是 修改任何内容)。

尝试一下

锻炼#go.m6.l4.e1
尝试:0加载中...

在 Point 上实现 String(),使其返回 (X,Y)。

正在加载编辑器...
显示提示

使用“fmt.Sprintf”格式化字段。

3 次尝试后可用的解决方案

锻炼#go.m6.l4.e2
尝试:0加载中...

在类型 Status (int) 上实现 String():0 → 'OK',1 → 'WARN',否则 → 'ERR'。

正在加载编辑器...
显示提示

打开接收器的值;请记住提供默认值。

3 次尝试后可用的解决方案

Quiz#go.m6.l4.e3
准备好

该方法必须满足哪个签名才能满足 fmt.Stringer?

Go
func (x T) ??? {}
答案选项

回顾

  • fmt.Stringer = interface { String() string }。
  • 实现它会改变 fmt.Print* 表示类型的方式。
  • 枚举 (iota)、域类型、代码/标识符的惯用语。
  • 切勿在 String() 内的 t 上调用 fmt.Sprintf("%s", t)(递归)。
  • 约定:String() 的值接收器(无突变,避免“方法集意外”)。