Menu
CoddyTech

fmt.Stringer 接口

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 75 课,共 107 课。

fmt.Stringer 接口决定打印时你的类型如何显示。它是 Go 中最常实现的接口之一,定义在 fmt 包中:

type Stringer interface {
    String() string
}

将值传递给 fmt.Println、使用 %v 的 fmt.Printf 或类似函数时,Go 会检查该类型是否实现了 Stringer。如果实现了,Go 就会调用 String() 方法来获取文本表示。

type Temperature struct {
    Celsius float64
}

func (t Temperature) String() string {
    return fmt.Sprintf("%.1f°C", t.Celsius)
}

func main() {
    temp := Temperature{Celsius: 23.5}
    fmt.Println(temp)  // 23.5°C
}

如果没有 String() 方法,打印时会显示默认的结构体格式:{23.5}。通过实现 Stringer,你可以精确控制类型的呈现方式。

这对于原始字段值无法清楚表达含义的类型尤其有用。Duration 类型可以显示为“2h 30m”,而不是显示原始纳秒数;User 类型则可以显示格式化后的名称,而不是显示它的所有字段。

challenge icon

挑战

简单

让我们构建一个时间跟踪系统,以人们容易理解的格式显示时长!你将实现 fmt.Stringer 接口,控制打印自定义类型时的显示方式。

你将把代码分放在两个文件中:

  • duration.go:定义与时间相关的类型,并为它们设置自定义字符串表示形式。

    创建一个 Duration 结构体,用整数存储总分钟数。实现 String() 方法,让它在打印时以易读的格式显示时长。例如,150 分钟显示为 2h 30m,45 分钟显示为 0h 45m。小时和分钟都必须始终显示。

    创建一个 Task 结构体,包含 Name(字符串)和 Time(Duration)字段。在 Task 上实现 String(),按 [Name]: [duration] 格式显示,其中时长使用其自身的 String 方法。

  • main.go:创建并显示任务及其时长。

    读取任务数量,然后为每个任务读取名称和分钟数。创建 Task 实例,并使用 fmt.Println 直接打印每个实例:Stringer 接口会自动处理格式。

    打印完所有任务后,计算并按以下格式打印所有任务的总时长:

    Total: [duration]

输入内容如下:

  • 第 1 行:任务数量
  • 后续各行:任务名称和分钟数(每个任务占两行)

例如,给定:

3
Code Review
90
Meeting
45
Documentation
120

输出应为:

Code Review: 1h 30m
Meeting: 0h 45m
Documentation: 2h 0m
Total: 4h 15m

再例如,给定:

2
Design
180
Testing
65

输出应为:

Design: 3h 0m
Testing: 1h 5m
Total: 4h 5m

实现 Stringer 后,你的类型就能与 Go 的打印函数无缝配合。你不会再看到像 {90} 这样的原始结构体字段,而是能得到对用户有意义的输出。

亲手试试

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 读取任务数量
	line, _ := reader.ReadString('\n')
	numTasks, _ := strconv.Atoi(strings.TrimSpace(line))

	// TODO:创建一个切片来存储任务
	// TODO:记录总分钟数

	for i := 0; i < numTasks; i++ {
		// 读取任务名称
		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		// 读取任务时长,单位为分钟
		minutesLine, _ := reader.ReadString('\n')
		minutes, _ := strconv.Atoi(strings.TrimSpace(minutesLine))

		// TODO:用名称和 Duration 创建一个 Task
		// TODO:输出任务(fmt.Println 会使用 Stringer 接口)
		// TODO:将分钟数加到总分钟数中
		_ = name
		_ = minutes
	}

	// TODO:按 "Total: Xh Ym" 格式输出总时长
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器