Golang学习笔记_19——Stringer

Golang学习笔记_16------Map
Golang学习笔记_17------方法
Golang学习笔记_18------接口


文章目录

    • Stringer
      • [1. 接口定义](#1. 接口定义)
      • [2. 实现Stringer接口](#2. 实现Stringer接口)
      • [3. 一个demo](#3. 一个demo)
      • [4. 使用场景](#4. 使用场景)
      • [5. 注意事项](#5. 注意事项)
      • [6. 一点回顾](#6. 一点回顾)
    • 源码

Stringer

在Go语言中,Stringer 是一个接口,定义在 fmt 包中。它用于自定义类型的字符串表示形式。当你希望你的类型在打印(例如使用 fmt.Print、fmt.Println 或 fmt.Sprintf 等函数)时具有特定的输出格式,你可以实现这个接口。

1. 接口定义

Stringer 接口非常简单,只包含一个方法:

go 复制代码
type Stringer interface {
    String() string
}

2. 实现Stringer接口

要实现 Stringer 接口,你需要为你的类型定义一个 String 方法,该方法返回一个字符串。

go 复制代码
type Student struct {
	Name string
	Age  int
	Num  int
}

func (stu *Student) String() string {
	return fmt.Sprintf("姓名:%s, 年龄:%d, 学号:%d", stu.Name, stu.Age, stu.Num)
}

func demo_1() {
	stu1 := Student{"张三", 20, 1001}
	stu2 := Student{"李四", 21, 1002}
	fmt.Println(stu1)
	fmt.Println(stu2)
}

测试方法

go 复制代码
func Test_demo_1(t *testing.T) {
	demo_1()
}

输出结果

复制代码
=== RUN   Test_demo_1
{张三 20 1001}
{李四 21 1002}
--- PASS: Test_demo_1 (0.00s)
PASS

3. 一个demo

go 复制代码
type IPAddr [4]byte

func (ip IPAddr) String() string {
	fmt.Println("worked")
	return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func demo_2() {

	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

测试方法

go 复制代码
func Test_demo_2(t *testing.T) {
	demo_2()
}

输出结果

go 复制代码
=== RUN   Test_demo_2
worked
googleDNS: 8.8.8.8
worked
loopback: 127.0.0.1
--- PASS: Test_demo_2 (0.00s)
PASS

4. 使用场景

  1. 调试和日志记录:当你需要打印复杂的数据结构时,通过实现 Stringer 接口,你可以提供易于阅读的字符串表示形式。
  2. 格式化输出:当你希望你的类型在打印时具有特定的格式时,可以实现 Stringer 接口来定义这种格式。
  3. 自定义类型:当你定义新的类型时,通过实现 Stringer 接口,你可以控制这些类型在打印时的行为。

5. 注意事项

  1. 实现 Stringer 接口时,确保 String 方法返回的是一个合理的字符串表示,以避免在打印时产生混淆。
  2. String 方法应该尽量简洁明了,避免在方法内部进行复杂的计算或操作,以提高性能。

6. 一点回顾

第一段代码

go 复制代码
type IPAddr [4]byte

func (ip IPAddr) String() string {
	fmt.Println("worked")
	return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func demo_2() {

	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

第二段代码

go 复制代码
type IPAddr [4]byte

func (ip *IPAddr) String() string {
	fmt.Println("worked")
	return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func demo_2() {

	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

问题:为什么在实现Stringer接口时,func (ip *IPAddr) String() string使用了指针形式,就失效了?

知识回顾

  1. 值接收者与指针接收者的区别:
    • 值接收者:方法操作的是值的副本。在方法内部对接收者(即方法操作的对象)所做的任何修改都不会影响到原始值。
    • 指针接收者:方法操作的是原始值的指针。在方法内部可以通过指针修改原始值的内容。
  2. 接口存储的是值的类型还是指针类型:
    • 当一个类型通过值接收者实现了一个接口时,该接口变量可以存储该类型的值。
    • 当一个类型通过指针接收者实现了一个接口时,该接口变量只能存储该类型的指针。

String 方法是用指针接收者 *IPAddr 实现的。这意味着只有 IPAddr 类型的指针才具有这个 String 方法。

声明了一个 mapstringIPAddr,这意味着 map 中存储的是 IPAddr 类型的值,而不是指针。当你从 map 中取值并赋值给 ip 变量时,ip 也是一个 IPAddr 类型的值。由于 ip 是一个值,它并不具有通过指针接收者实现的 String 方法。因此,在这种情况下,fmt.Printf 使用的是默认的格式化行为,而不是你自定义的 String 方法。

源码

go 复制代码
// stringer_demo.go 文件
package stringer_demo

import "fmt"

type Student struct {
	Name string
	Age  int
	Num  int
}

func (stu *Student) String() string {
	return fmt.Sprintf("姓名:%s, 年龄:%d, 学号:%d", stu.Name, stu.Age, stu.Num)
}

func demo_1() {
	stu1 := Student{"张三", 20, 1001}
	stu2 := Student{"李四", 21, 1002}
	fmt.Println(stu1)
	fmt.Println(stu2)
}

type IPAddr [4]byte

func (ip IPAddr) String() string {
	fmt.Println("worked")
	return fmt.Sprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3])
}

func demo_2() {

	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}
go 复制代码
// stringer_demo_test.go 文件
package stringer_demo

import "testing"

func Test_demo_1(t *testing.T) {
	demo_1()
}

func Test_demo_2(t *testing.T) {
	demo_2()
}
相关推荐
qq_284274052 小时前
机械设计基础·滚动轴承全解(类型·代号·选型·寿命)+轮系与蜗杆蜗轮传动
笔记·学习·自动化·制造
ACP广源盛139246256737 小时前
硬件选型笔记:国产 PCIe Gen2.1 交换芯片 IX6024@ACP,工控与存量设备国产化改造方案评估
笔记·硬件架构·硬件工程·pcie·国产芯片
2603_965148117 小时前
社区与学习资源:从入门到精通的API选品学习路径
数据库·人工智能·python·学习·数据挖掘
文人sec7 小时前
SQL基础入门笔记 | 从零基础到掌握核心查询语句
数据库·笔记·sql
摇滚侠9 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记
kaixin_啊啊9 小时前
【用于更准确 G×E 基因组预测的大规模作物数据集与基于深度学习的多模态融合框架】论文学习
人工智能·深度学习·学习
xier_ran9 小时前
【infra之路】AI 编译器学习笔记(六):重拾 C++——lambda 与 MLIR 中的回调
c++·笔记·学习
志尊宝9 小时前
Vue3 零基础每日笔记(078):首屏加载速度优化汇总——清单化方案
笔记
别动我齐刘海11 小时前
Linux 系统调试学习2
linux·运维·c++·人工智能·学习·机器学习·机器人
m4Rk_11 小时前
【论文阅读】Agent 记忆机制(99):PrisMem——从整体分数搜索走向能力驱动的记忆自进化
论文阅读·人工智能·学习·开源·github