news 2026/4/18 10:43:19

Go进阶之方法集合接口实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Go进阶之方法集合接口实现

自定义类型的方法和接口都是Go语言中的概念.并且他们之间存在千丝万缕的关系.示例:

package main type Interface interface { M1() M2() } type T struct{} func (t T) M1() {} func (t *T) M2() {} func main() { var t T var pt *T var i Interface i = t i = pt }

上边的例子没有通过编译器检查.编译器给出的信息是:不能使用变量t给接口类型变量

i赋值.因为t没有实现Interface接口方法集合中的M2()方法.下面会慢慢解除疑惑.

1.方法集合:

上一篇文章说过receiver类型除了考量是否需要对类型实例进行修改 类型实例复制

导致的性能损耗之外.还有一个因素就是类型是否需要实现某个接口类型.

Go语言的一个创新是.自定义类型与接口之间实现关系是松耦合的.如果某个自定义类

型T的方法集合是某个接口类型的方法集合的超集.那么就说类型T实现了该接口.并且

该类型T的变量可以被赋值给该接口类型的变量.即方法集合决定接口实现.

方法集合是Go语言中一个重要的概念.在为接口类型变量赋值 使用结构体嵌入 接口

嵌入 类型别名和方法表达式等时都会用到方法集合.像胶水一样自定义类型与接口隐

式的粘结在一起.

要判断一个自定义类型是否实现了某接口类型.首先要识别出自定义类型的方法集合

和接口类型的方法集合.有时候并不明显.可以通过一个工具函数方便的输出自定义类

型或接口的方法集合.

工具函数:
func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s 方法个数为0", elemTyp) return } fmt.Printf("%s 集合\n", elemTyp) for i := 0; i < n; i++ { fmt.Println("-", elemTyp.Method(i).Name) } fmt.Printf("\n") }
把开头的代码调用如上工具类:
package main import ( "fmt" "reflect" ) type Interface interface { M1() M2() } type T struct{} func (t T) M1() {} func (t *T) M2() {} func main() { var t T var pt *T DumpMethodSet(&t) DumpMethodSet(&pt) DumpMethodSet((*Interface)(nil)) } func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s 方法个数为0", elemTyp) return } fmt.Printf("%s 集合\n", elemTyp) for i := 0; i < n; i++ { fmt.Println("-", elemTyp.Method(i).Name) } fmt.Printf("\n") }
执行结果:

从执行结果中可以看出.var t T的方法集合中并不是Interface的超集.所以无法进行

赋值.

2.类型嵌入与方法集合:

Go语言的设计哲学之一是偏好组合.Go语言支持用组合的思想来实现一些面向对象

领域经典的机制.比如继承.

与接口类型和结构体类型相关的类型嵌入有三种组合.在接口类型中嵌入接口类型. 在

结构体类型中嵌入接口类型. 在结构体类型中嵌入结构体类型.

2.1在接口类型中嵌入接口类型:

按Go语言惯例.接口类型中仅包含少量方法.并且常常仅有一个方法.通过在接口类型

中嵌入其他接口类型实现接口的组合.比如io包中的ReadWriter

ReadWriteCloser 等接口类型就是通过嵌入Reader Writer Closer三个基本接口

类型形成的.源码如下:

type Reader interface { Read(p []byte) (n int, err error) } type Writer interface { Write(p []byte) (n int, err error) } type Closer interface { Close() error }

上面为三个基本接口.

type ReadWriter interface { Reader Writer } type ReadCloser interface { Reader Closer } type WriteCloser interface { Writer Closer } type ReadWriteCloser interface { Reader Writer Closer }

再来看看通过嵌入接口类型后新接口类型的方法集合,以Go标准库中的几个接口为例

子.

示例如下:
func main() { DumpMethodSet((*io.Writer)(nil)) DumpMethodSet((*io.Reader)(nil)) DumpMethodSet((*io.Closer)(nil)) DumpMethodSet((*io.ReadWriter)(nil)) DumpMethodSet((*io.ReadWriteCloser)(nil)) }
执行结果:

通过输出结果可知.通过嵌入接口类型而创建的新接口类型的方法集合包括了被嵌入

接口类型的方法集合.

在Go1.14之前版本有一个约束.被嵌入接口类型的方法集合不能有交集.同时被嵌入

的接口类型的方法集合中的方法不能与新接口中其他方法同名.

type Interface1 interface { M1() } type Interface2 interface { M1() M2() } type Interface3 interface { Interface1 Interface2 } type Interface4 interface { Interface2 M2() }

Go1.14版本开始去掉了该约束.

2.2在结构体类型中嵌入接口类型:

在结构体类型中嵌入接口类型后.该结构体类型的方法集合中将包含被嵌入接口类型

的方法集合.

示例:
package main import ( "fmt" "reflect" ) type Interface interface { M1() M2() } type T struct { Interface } func (T) M3() { } func main() { DumpMethodSet((*Interface)(nil)) var t T var pt *T DumpMethodSet(&t) DumpMethodSet(&pt) }
运行结果:

输出的结果与预期一致.如果当结构体类型中嵌入多个接口类型且这些接口类型的方

法集合存在交集时.结果就会出现不一样.

Go选择方法次序:

1).优先选择结构体自身实现的方法.

2).如果结构体自身并未实现.那么将查找结构体中的嵌入接口类型的方法集合中是否

有方法.如果有.则提升为结构体方法.

示例如下:
package main import ( "fmt" "reflect" ) type Interface interface { M1() M2() } type T struct { Interface } func (T) M1() { fmt.Println("struct M1") } type S struct{} func (S) M1() { fmt.Println("s M1") } func (S) M2() { fmt.Println("s M2") } func main() { var t = T{ Interface: S{}, } t.M1() t.M2() }
输出结果:

如果结构体嵌入了多个接口类型且这些接口类型的方法集合存在交集.Go编译器将会

报错.除非结构体自己实现了交集中所有的方法.

示例如下:
package main import ( "fmt" "reflect" ) type Interface interface { M1() M2() M3() } type Interface1 interface { M1() M2() M4() } type T struct { Interface Interface1 } func main() { t := new(T) t.M1() t.M2() }

可以看到编译器给出错误提示.编译器在t.M1和t.M2出现分歧.不知道该选择哪个.

修证示例:
package main import ( "fmt" "reflect" ) type Interface interface { M1() M2() M3() } type Interface1 interface { M1() M2() M4() } type T struct { Interface Interface1 } func (t *T) M1() { } func (t *T) M2() { } func main() { t := new(T) t.M1() t.M2() }

结构体类型在嵌入某个接口类型的同时.也实现了这个接口.这一特性在单元测试中特

别有用.(后续单元测试的时候在详情举例).

2.3在结构体类型中嵌入结构体类型:

在结构体中嵌入结构体提供了一种实现继承的手段.外部的结构体类型T可以继承嵌入

的结构体类型的所有方法.无论是T类型的变量实例还是*T类型变量实例.都可以调用

所有继承的方法.

示例:
package main import ( "fmt" "reflect" ) type T1 struct { } type T2 struct { } func (t T1) T1M1() { fmt.Println("T1M1") } func (t T1) T1M2() { fmt.Println("T1M2") } func (t *T1) T1M3() { fmt.Println("T1M3") } func (t T2) T2M1() { fmt.Println("T2M1") } func (t T2) T2M2() { fmt.Println("T2M2") } func (t *T2) T2M3() { fmt.Println("T2M3") } type T struct { T1 T2 } func main() { t := T{ T1: T1{}, T2: T2{}, } fmt.Println("call method through t") t.T1M1() t.T1M2() t.T1M3() t.T2M1() t.T2M2() t.T2M3() fmt.Println("\ncall method through pt") pt := &t pt.T1M1() pt.T1M2() pt.T1M3() pt.T2M1() pt.T2M2() pt.T2M3() var t1 T1 var pt1 *T1 DumpMethodSet(&t1) DumpMethodSet(&pt1) var t2 T2 var pt2 *T2 DumpMethodSet(&t2) DumpMethodSet(&pt2) } func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s 方法个数为0", elemTyp) return } fmt.Printf("%s 集合\n", elemTyp) for i := 0; i < n; i++ { fmt.Println("-", elemTyp.Method(i).Name) } fmt.Printf("\n") }
执行结果:

从结果可以看出.

T类型的方法集合=T1的方法集合+*T2的方法集合.

*T类型的集合=*T1的方法集合+*T2的方法集合.

3.defined类型的方法集合.

Go语言支持基于已有的类型创建新类型.示例如下:

type MyInterface T1 type MyInterface2 T2

已有的类型(比如上面的T1 T2)被称为underlying类型.新类型被称为defined类型.

新定义的类型和原类型方法集合会有什么不同.

示例如下:
package main import ( "fmt" "reflect" ) type T struct{} func (t T) M1() { } func (t *T) M2() { } type Interface interface { M1() M2() } type T1 T type Interface1 Interface func main() { var t T var pt *T var t1 T1 var pt1 *T1 DumpMethodSet(&t) DumpMethodSet(&pt) DumpMethodSet(&t1) DumpMethodSet(&pt1) DumpMethodSet((*Interface)(nil)) DumpMethodSet((*Interface1)(nil)) } func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s 方法个数为0", elemTyp) return } fmt.Printf("%s 集合\n", elemTyp) for i := 0; i < n; i++ { fmt.Println("-", elemTyp.Method(i).Name) } fmt.Printf("\n") }
执行结果:

从结果来看.Go对于分别基于接口类型和自定义非接口类型创建的defined类型给出

了不一样的结果.

基于接口类型创建的defined类型与原接口类型的方法集合是一致的.

而基于自定义非接口类型创建的defiend类型并没有继承原类型的方法集合.新的

dfiend类型的方法集合是空的.

4.类型别名的方法集合:

类型别名与原类型几乎是等价的.但是在方法集合上与原类型是否有区别呢.

示例:
package main import ( "fmt" "reflect" ) type T struct { } func (T) M1() { fmt.Println("M1") } func (*T) M2() { fmt.Println("M2") } type Interface interface { M1() M2() } type T1 = T type Interface1 = Interface func main() { var t T var pt *T var t1 T1 var pt1 *T1 DumpMethodSet(&t) DumpMethodSet(&pt) DumpMethodSet(&t1) DumpMethodSet(&pt1) DumpMethodSet((*Interface)(nil)) DumpMethodSet((*Interface1)(nil)) } func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s 方法个数为0", elemTyp) return } fmt.Printf("%s 集合\n", elemTyp) for i := 0; i < n; i++ { fmt.Println("-", elemTyp.Method(i).Name) } fmt.Printf("\n") }
执行结果:

从输出结果可以看出.函数甚至都无法识别出类型别名.无论类型别名还是原类型输出

的都是一样的方法集合.由此可以知道.无论别名与原类型拥有完全相同的方法集合.无

论原类型是接口类型还是非接口类型.

让风吹过我耳廓.烦恼都吹破.

如果大家喜欢我的分享的话.可以关注我的微信公众号

念何架构之路

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 12:56:31

如何高效保存网络资源?3个创新方法让你实现知识资产化管理

如何高效保存网络资源&#xff1f;3个创新方法让你实现知识资产化管理 【免费下载链接】bilibili-downloader B站视频下载&#xff0c;支持下载大会员清晰度4K&#xff0c;持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 你是否曾遇到过…

作者头像 李华
网站建设 2026/4/18 2:08:00

告别失效收藏夹烦恼:高效下载与无损保存B站视频的完整指南

告别失效收藏夹烦恼&#xff1a;高效下载与无损保存B站视频的完整指南 【免费下载链接】bilibili-downloader B站视频下载&#xff0c;支持下载大会员清晰度4K&#xff0c;持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloader 还在为珍藏的学…

作者头像 李华
网站建设 2026/4/17 23:14:22

prompt模板设计技巧:提升Unsloth训练效果

prompt模板设计技巧&#xff1a;提升Unsloth训练效果 在使用Unsloth进行大语言模型微调时&#xff0c;很多人把注意力集中在LoRA参数、学习率或硬件配置上&#xff0c;却忽略了最基础也最关键的环节——prompt模板的设计。一个精心设计的prompt模板&#xff0c;不是简单的文本…

作者头像 李华
网站建设 2026/4/18 2:02:53

如何用Mac Mouse Fix彻底释放鼠标潜能:从入门到精通的实用指南

如何用Mac Mouse Fix彻底释放鼠标潜能&#xff1a;从入门到精通的实用指南 【免费下载链接】mac-mouse-fix Mac Mouse Fix - A simple way to make your mouse better. 项目地址: https://gitcode.com/GitHub_Trending/ma/mac-mouse-fix Mac Mouse Fix是一款免费工具&am…

作者头像 李华
网站建设 2026/4/18 1:59:51

EverMemOS 对话提取关键信息触发条件分析

EverMemOS 对话提取关键信息触发条件分析 EverMemOS 系统中&#xff0c;对话提取关键信息的触发条件主要分为三大类&#xff1a;强制触发条件、语义触发条件和流程触发条件。以下是详细分析&#xff1a; 一、强制触发条件 当对话数据达到系统设定的硬限制时&#xff0c;会强制触…

作者头像 李华
网站建设 2026/4/17 12:17:09

YOLOv13推理延迟低至2ms!实测数据曝光

YOLOv13推理延迟低至2ms&#xff01;实测数据曝光 在自动驾驶感知系统的毫秒级决策链路中&#xff0c;一个目标检测模型的推理耗时若多出3毫秒&#xff0c;就可能让车辆在60km/h速度下多行驶5厘米——这已接近紧急避障的安全阈值。当行业还在为YOLOv12的2.8ms延迟欣喜时&#…

作者头像 李华