go语言中的接口

深入掌握Go语言中的空接口: interface{} 在这篇教程中,我们深入探讨了 Go 语言中的空接口 interface{}。从定义和基本用法,到类型断言和类型开关,再到最佳实践和编译器处理机制,我们全面解析了空接口的各个方面。空接口作为 Go 语言中的顶级接口,提供了极大的灵活性,可以处理任意类型的值。通过类型断言和类型开关,我们可以安全地处理不同类型的值,同时保持代码的清晰和高效。希望这篇教程能帮助你更好地理解和应用空接口,提升你的 Go 语言编程技能。继续探索,不断进步,享受编程的乐趣吧! 阅读详情

接口

接口是Go语言整个类型系统的基石。

其他语言的接口

Go语言的接口并不是其他语言(C++、Java、C#等)中所提供的接口概念。
在Go语言出现之前,接口主要作为不同组件之间的契约存在。对契约的实现是强制的,你必须声明你的确实现了该接口。为了实现一个接口,你需要从该接口继承:

interface IFoo { 
 void Bar(); 
} 
class Foo implements IFoo { // Java文法
 // ... 
} 
class Foo : public IFoo { // C++文法
 // ... 
} 
IFoo* foo = new Foo; 

即使另外有一个接口IFoo2实现了与IFoo完全一样的接口方法甚至名字也叫IFoo只不过位于不同的名字空间下,编译器也会认为上面的类Foo只实现了IFoo而没有实现IFoo2接口。
这类接口我们称为侵入式接口。“侵入式”的主要表现在于实现类需要明确声明自己实现了某个接口。

设想我们现在要实现一个简单搜索引擎(SE),它需要依赖两个模块,一个是哈希表(HT),一个是HTML分析器(HtmlParser)。搜索引擎的实现者认为,SE对HT的依赖是确定性的,所以不需要在SE和HT之间定义接口,而是直接通过import(或者include)的方式使用HT;而模块SE对HtmlParser的依赖是不确定的,未来可能需要有WordParser、PdfParser等模块来替代HtmlParser,以达到不同的业务要求。为此,他定义了SE和HtmlParser之间的接口,在模块SE中通过接口调用方式间接引用模HtmlParser。
应当注意到,接口的需求方是SE,只有SE才知道接口应该定义成什么样子,但是接口的实现方是HtmlParser。基于模块设计的单向依赖原则,模块HtmlParser实现自身的业务时,不应该关心某个具体使用方的要求。HtmlParser在实现的时候,甚至还不知道未来有一天SE会用上它。期望模块HtmlParser能够知道需求方需要的所有接口,并提前声明实现这些接口是不合理的。

同样的道理发生在SE自己身上。SE并不能够预计未来会有哪些需求方会用到自己,并且实现它们所要求的接口。
这个问题在设计标准库时变得更加突出,比如我们实现了File类,它有下面这些方法:

Read(buf []byte) (n int, err error) 
Write(buf []byte) (n int, err error) 
Seek(off int64, whence int) (pos int64, err error) 
Close() error 

那么,到底是应该定义一个IFile接口,还是应该定义一系列的IReader、IWriter、
ISeeker、ICloser接口,然后让File从它们继承好呢?脱离了实际的用户场景,讨论这两个

非侵入式接口

在Go语言中,一个类只需要实现了接口要求的所有函数,我们就说这个类实现了该接口,例如:

type File struct { 
 // ... 
} 
func (f *File) Read(buf []byte) (n int, err error) 
func (f *File) Write(buf []byte) (n int, err error) 
func (f *File) Seek(off int64, whence int) (pos int64, err error) 
func (f *File) Close() error 

这里我们定义了一个File类,并实现有Read()、Write(),Seek()、Close()等方法。设想我们有如下接口:

type IFile interface { 
 Read(buf []byte) (n int, err error) 
 Write(buf []byte) (n int, err error) 
 Seek(off int64, whence int) (pos int64, err error) 
 Close() error 
} 
type IReader interface { 
 Read(buf []byte) (n int, err error) 
} 
type IWriter interface { 
 Write(buf []byte) (n int, err error) 
} 
type ICloser interface { 
 Close() error 
} 

尽管File类并没有从这些接口继承,甚至可以不知道这些接口的存在,但是File类实现了这些接口,可以进行赋值:

var file1 IFile = new(File) 
var file2 IReader = new(File) 
var file3 IWriter = new(File) 
var file4 ICloser = new(File) 

Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。其一,Go语言的标准库,再也不需要绘制类库的继承树图。你一定见过不少C++、Java、C# 类库的继承树图。这里给个Java继承树图:http://docs.oracle.com/javase/1.4.2/docs/api/overview-tree.html
在Go中,类的继承树并无意义,你只需要知道这个类实现了哪些方法,每个方法是啥含义就足够了。

其二,实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。接口由使用方按需定义,而不用事前规划。

其三,不用为了实现一个接口而导入一个包,因为多引用一个外部的包,就意味着更多的耦合。接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。

接口赋值

接口赋值在Go语言中分为如下两种情况:
 将对象实例赋值给接口;
 将一个接口赋值给另一个接口。
先讨论将某种类型的对象实例赋值给接口,这要求该对象实例实现了接口要求的所有方法,例如一个Integer类型,如下:

type Integer int
func (a Integer) Less(b Integer) bool { 
 return a < b 
} 
func (a *Integer) Add(b Integer) { 
 *a += b 
} 

相应地,我们定义接口LessAdder,如下:

type LessAdder interface { 
 Less(b Integer) bool
 Add(b Integer) 
} 

现在有个问题:假设我们定义一个Integer类型的对象实例,怎么将其赋值给LessAdder接口呢?应该用下面的语句(1),还是语句(2)呢?

var a Integer = 1 
var b LessAdder = &a ... (1) 
var b LessAdder = a ... (2) 

答案是应该用语句(1)。原因在于,Go语言可以根据下面的函数:

func (a Integer) Less(b Integer) bool 

自动生成一个新的Less()方法:

func (a *Integer) Less(b Integer) bool { 
 return (*a).Less(b) 
} 

这样,类型*Integer就既存在Less()方法,也存在Add()方法,满足LessAdder接口。而从另一方面来说,根据

func (a *Integer) Add(b Integer) 

这个函数无法自动生成以下这个成员方法:

func (a Integer) Add(b Integer) { 
 (&a).Add(b) 
} 

因为(&a).Add()改变的只是函数参数a,对外部实际要操作的对象并无影响,这不符合用户的预期。所以,Go语言不会自动为其生成该函数。因此,类型Integer只存在Less()方法,缺少Add()方法,不满足LessAdder接口,故此上面的语句(2)不能赋值。
为了进一步证明以上的推理,我们不妨再定义一个Lesser接口,如下:

type Lesser interface { 
 Less(b Integer) bool
} 

然后定义一个Integer类型的对象实例,将其赋值给Lesser接口:

var a Integer = 1 
var b1 Lesser = &a ... (1) 
var b2 Lesser = a ... (2) 

正如我们所料的那样,语句(1)和语句(2)均可以编译通过。
我们再来讨论另一种情形:将一个接口赋值给另一个接口。在Go语言中,只要两个接口拥有相同的方法列表(次序不同不要紧),那么它们就是等同的,可以相互赋值。
下面我们来看一个示例,这是第一个接口:

package one 
type ReadWriter interface { 
 Read(buf []byte) (n int, err error) 
 Write(buf []byte) (n int, err error) 
} 

第二个接口位于另一个包中:

package two 
type IStream interface { 
 Write(buf []byte) (n int, err error) 
 Read(buf []byte) (n int, err error) 
} 

这里我们定义了两个接口,一个叫one.ReadWriter,一个叫two.Istream,两者都定义了Read()、Write()方法,只是定义次序相反。one.ReadWriter先定义了Read()再定义了Write(),而two.IStream反之。

在Go语言中,这两个接口实际上并无区别,因为:
 任何实现了one.ReadWriter接口的类,均实现了two.IStream;
 任何one.ReadWriter接口对象可赋值给two.IStream,反之亦然;
 在任何地方使用one.ReadWriter接口与使用two.IStream并无差异。
以下这些代码可编译通过:

var file1 two.IStream = new(File) 
var file2 one.ReadWriter = file1 
var file3 two.IStream = file2 

接口赋值并不要求两个接口必须等价。如果接口A的方法列表是接口B的方法列表的子集,那么接口B可以赋值给接口A。例如,假设我们有Writer接口:

type Writer interface { 
 Write(buf []byte) (n int, err error) 
} 

就可以将上面的one.ReadWriter和two.IStream接口的实例赋值给Writer接口:

var file1 two.IStream = new(File) 
var file4 Writer = file1 

但是反过来并不成立:

var file1 Writer = new(File) 
var file5 two.IStream = file1 // 编译不能通过

这段代码无法编译通过,原因是显然的:file1并没有Read()方法。

接口查询

查询语法,代码如下:

var file1 Writer = ... 
if file5, ok := file1.(two.IStream); ok { 
 ... 
} 

这个if语句检查file1接口指向的对象实例是否实现了two.IStream接口,如果实现了,则执行特定的代码。
接口查询是否成功,要在运行期才能够确定。

类型查询

在Go语言中,还可以更加直截了当地询问接口指向的对象实例的类型,例如:

var v1 interface{} = ... 
switch v := v1.(type) { 
 case int: // 现在v的类型是int 
 case string: // 现在v的类型是string 
 ... 
} 

就像现实生活中物种多得数不清一样,语言中的类型也多得数不清,所以类型查询并不经常使用。它更多是个补充,需要配合接口查询使用,例如:

type Stringer interface { 
 String() string
} 
func Println(args ...interface{}) { 
 for _, arg := range args { 
 switch v := v1.(type) { 
 case int: // 现在v的类型是int 
 case string: // 现在v的类型是string 
 default: 
 if v, ok := arg.(Stringer); ok { // 现在v的类型是Stringer 
 val := v.String() 
 // ... 
 } else { 
 // ... 
 } 
 } 
 } 
} 

当然,Go语言标准库的Println()比这个例子要复杂很多,我们这里只摘取其中的关键部
分进行分析。对于内置类型,Println()采用穷举法,将每个类型转换为字符串进行打印。对于更一般的情况,首先确定该类型是否实现了String()方法,如果实现了,则用String()方法将其转换为字符串进行打印。否则,Println()利用反射功能来遍历对象的所有成员变量进行打印。

接口组合

像之前介绍的类型组合一样,Go语言同样支持接口组合。我们已经介绍过Go语言包中io.Reader接口和io.Writer接口,接下来我们再介绍同样来自于io包的另一个接口io.ReadWriter:

// ReadWriter接口将基本的Read和Write方法组合起来
type ReadWriter interface { 
 Reader 
 Writer 
} 
这个接口组合了Reader和Writer两个接口,它完全等同于如下写法:
type ReadWriter interface { 
 Read(p []byte) (n int, err error) 
 Write(p []byte) (n int, err error) 
} 

因为这两种写法的表意完全相同:ReadWriter接口既能做Reader接口的所有事情,又能做Writer接口的所有事情。在Go语言包中,还有众多类似的组合接口,比如ReadWriteCloser、ReadWriteSeeker、ReadSeeker和WriteCloser等。
可以认为接口组合是类型匿名组合的一个特定场景,只不过接口只包含方法,而不包含任何成员变量。

Any类型

由于Go语言中任何对象实例都满足空接口interface{},所以interface{}看起来像是可
以指向任何对象的Any类型,如下:

var v1 interface{} = 1 // 将int类型赋值给interface{} 
var v2 interface{} = "abc" // 将string类型赋值给interface{} 
var v3 interface{} = &v2 // 将*interface{}类型赋值给interface{} 
var v4 interface{} = struct{ X int }{1} 
var v5 interface{} = &struct{ X int }{1} 

当函数可以接受任意的对象实例时,我们会将其声明为interface{},最典型的例子是标
准库fmt中PrintXXX系列的函数,例如:
func Printf(fmt string, args …interface{})
func Println(args …interface{})
…

Go 语言中的接口详解 在 Go 中,接口(interface)是一组方法的集合。任何类型只要实现了接口中定义的所有方法,就可以被认为是该接口类型的实例。在 Go 中,接口的实现是隐式的,不需要显示地声明类型实现了某个接口,只要方法符合即可。接口的定义非常简单,只需声明方法签名即可。例如,我们可以定义一个描述几何图形的接口Shape,该接口包含一个Area// 定义接口在这个例子中,Shape 接口定义了一个 Area 方法,返回值类型为 float64。任何具有 Area 方法的类型都被认为是 Shape 类型。 阅读详情

相关推荐

【Shader】Unity批量更换Shader

在Unity开发中,因为渲染管线问题,经常会遇到需要大量更改shader的问题,本篇文章讲述批量更换Shader的工具。

憨辰 1468

1.Spring设计理念(最小侵入式,依赖注入,切面)

本篇博客用实际的例子以通俗易懂的形式解释了什么是侵入式编程,什么是非侵入式编程,什么是依赖注入,以及我们为什么需要面向切面编程。

c1776167012的博客 1532

关于StartAI本地部署相关问题解答

很多小伙伴们都有接入自己本地SD的需求,对此小编整理了一些相关问题~

HDJ9387的博客 940

Go 语言中的接口

Go语言接口采用鸭子类型设计哲学,强调"能做什么"而非"是什么",通过隐式实现实现灵活的多态。接口定义只需方法声明,类型自动实现所有方法即满足接口。空接口(interface{})可存储任意类型。实践中应定义小而精的接口,允许单一类型实现多个接口,并通过接口组合与结构体嵌入实现依赖注入,有效降低代码耦合度。这种设计结合了静态类型安全和动态语言灵活性,是Go构建可扩展系统的核心机制。

一切总会归于平淡 1063

Openclaw Onboarding向导全攻略——如何配置你的第一个AI助手

很多新手在成功运行 `openclaw --version` 后,面对黑底白字的终端就陷入了迷茫:“安装好了,然后呢?” 本篇我们就彻底解决这个问题,带你完整走完 `openclaw onboard` 交互式初始化配置向导的每一步,从“安装完成”到“真正能用”,只差这一个向导的距离。 本文为2026年3月最新版,适配OpenClaw v2026.2.x 稳定版,所有步骤均经过实测验证,可直接落地。

夏日清风 1919

瑞芯微RK3568开发板实战:Android11系统parameter.txt文件配置全解析(附常见问题排查)

本文深入解析了瑞芯微RK3568开发板在Android 11系统下parameter.txt文件的配置要点与实战技巧。作为BootLoader启动阶段的核心蓝图,该文件决定了硬件初始化、分区布局及电源管理。文章详细拆解了身份标识、GPIO控制(PWR_HLD)、命令行参数及分区表等关键字段的配置逻辑,并针对RK3568平台与Android 11特性提供了专项适配策略,最后附带了启动失败、分区异常等常见问题的深度排查方法。

php55的博客 899

【Go】Go 语言中的接口

如果你是一个大佬,请直接看四。看不懂再从一听我啰嗦。一、接口二、定义接口三、实例四、Go语言中文文档中定义的接口参考链接 一、接口 Go 语言提供了另外一种数据类型:接口(interface),它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。 接口是一组方法签名的集合,然后我们可以定义一个结构体实现该接口所有方法。因此,接口就是定义了对象的行为。 例如,结构体Dog可以walk和bark, 如果一个接口声明了walk和bark的方法签名,而Dog实现了walk和bar

weixin_44211968的博客 1545

高阶玩法——Dify生成的数据自动写入mysql数据库

采用自定义工具方式,利用 python编写一个服务sql_save_post,将接收到的内容写入指定数据库的表中,并为该服务撰写一个 OpenAPI 的 schema。配置工作流:我们以写入天气预报为例,在生成天气预报内容后,连接自定义工具“sql_save_post”,将所需的字段配置完成后,实现将生成的内容自动写入mysql数据库中。编写OpenAPI的schema:在dify平台的工具栏目,创建自定义工具,输入工具名称“sql_save_post”。工具:sql_save_post;

qq_44493378的博客 8544

golang 切片 接口_一文搞懂Go语言中的切片排序

本文首发于“Gopher部落”知识星球!切片是Go语言中引入的用于在大多数场合替代数组的语法元素。切片是长度可变的同类型元素序列,它不支持存储不同类型的元素,当然如果你非用sl := []interface{}{"hello", 11, 3.14}来抬杠^_^,那就另当别论。有序列的地方就有排序的需求。在各种排序算法都已经成熟的今天,我们完全可以针对特定元素类型的切片手写排序函数/方法,但多数情况...

weixin_40005330的博客 306

未隔离电源设备使用usb转串口调试导致串口模块爆炸烧坏事故记录

这个事就发生在前两天,我们公司的wifi模块被客户用到了咖啡机上,客户寄了一台整机给我们协助调试,还送了几包咖啡豆,本想着还能打个咖啡喝一喝的,但就在咖啡机刚寄过来第一天,就出事了。而这个事故的发生,竟然仅仅是因为同事接了个串口模块到电脑调试打印导致的。谁也想不到,这个3.3V的供电的wifi模块居然还有这么大的威力。直接将usb转TTL串口模块的转换芯片给爆开了,咖啡机也给弄坏了,据同事描述当时情况就只见到一片火光和一声巨响,状况比较惨烈,所幸的是笔记本电脑没有烧坏,但是直接给崩蓝屏了。 ...

fangye945a的博客 6197

大数据工程中的数据血缘分析工具比较

数据血缘(Data Lineage)是大数据治理中的核心组成部分,它描述了数据从源头到目标的完整流动路径。系统介绍数据血缘的核心概念和技术原理比较主流开源和商业数据血缘工具的功能特点提供数据血缘分析的实践指南和选型建议本文首先介绍数据血缘的基本概念,然后深入分析其技术原理和实现方法,接着比较主流工具的特点,最后通过实际案例展示应用场景。数据血缘(Data Lineage): 描述数据从源头到目标的完整流动路径,包括所有转换和处理步骤元数据(Metadata)

大数据洞察的博客 900

【Go语言】Go语言中的接口类型

Go语言提倡面向接口编程。接口是一个或多个方法签名的集合。任何类型的方法集中只要拥有该接口'对应的全部方法'签名。就表示它 "实现" 了该接口,无须在该类型上显式声明实现了哪个接口。这称为Structural Typing。所谓对应方法,是指有相同名称、参数列表 (不包括参数名) 以及返回值。当然,该类型还可以有其他方法。接口只有方法声明,没有实现,没有数据字段。接口可以匿名嵌入其他接口,或嵌入到结构中。

Python、Golang、大数据 1734

绿米Aqara S1【妙控开关 S1E】版本降级或升级自定义固件的方法

绿米Aqara S1【妙控开关 S1E】版本降级或升级自定义固件的方法

LeeKwen的专栏 2176

GO语言中的接口(interface)

在Go语言中,接口(interface)是方法的集合,它允许我们定义一组方法但不实现它们,任何类型只要实现了这些方法,就被认为是实现了该接口。接口更重要的作用在于多态实现,它允许程序以多态的方式处理不同类型的值。接口体现了程序设计的多态和高内聚、低耦合的思想。

lp15929801907的博客 4321

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

GO语言中的接口

Mr.yang 278

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Go语言中的接口

1、接口时用来定义行为的类型 对接口值方法的调用会执行接口值里存储的用户定义的类型的值对应的方法 eg: package main import "fmt" type notifier interface { notify() } type user struct { name string email string } func (u *user) notify() { ...

weixin_41670928的博客 559

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Go 语言中的 Interface 接口详解

本文聚焦于 Go 语言中的 interface 接口,详细阐述其基本概念、使用方法、实现原理。通过丰富的代码示例与实际项目案例,深入剖析接口在 Go 语言编程中的应用场景。同时,对接口相关的重点知识进行拓展,帮助读者全面理解并掌握 Go 语言中接口的强大功能,提升编程能力。

Tekin 是深耕技术 20 年的全栈实战派专家,精通 Go/Python/Java 等多语言开发。博客专注技术原理与实战结合,深度解析 Python 高阶编程、Go 语言架构、数据库优化等硬核内容。涵盖并发编程、机器学习、云原生等前沿领域,通过真实案例拆 1700

6.2 如何理解Go语言中的接口

深入理解Go语言接口与传统语言中类的区别。

老孙正经胡说 345

Go语言中的空接口

空接口的概念 该接⼝中没有任何的⽅法,任意类型都可以实现该接⼝。 空接口的定义 包含0个method的interface。 type xxx interface {} 空接口的使用场景 ⽤空接⼝表示任意数据类型,类似于java中的object。 println的参数就是空接⼝; 定义⼀个map: key是string, value是任意数据类型; 定义⼀个切⽚,其中存储任意类型的数据。     案例如下: //myInterfaceNull.go // myInterfaceNull proje

sanqima的专栏 1134

Go语言中的接口是什么?它们与其他语言中的接口有何不同?

在 Go 语言中,接口(interface)是一种抽象类型,它定义了一组方法的集合。接口提供了一种方式来指定对象应该具有的行为,而不需要关心具体类型。任何类型只要实现了接口中定义的方法,就被认为是该接口的实现类型。接口中定义了一组方法签名,但不包含方法的具体实现。通过这种方式,我们可以调用。接口中定义的方法,实现了多态的效果。然后,我们定义了一个。接口的定义非常简单,只需要使用。在这个例子中,我们定义了一个。类型的对象,并将其赋值给了。函数中,我们创建了一个。结构体,并分别实现了。

克里斯蒂亚诺更新的博客 273

Go语言中的接口与多态性实现

接口和多态性是Go语言中非常重要的概念,它们为我们提供了实现灵活性和可扩展性的手段。在实际开发中,我们应该充分利用接口和多态性的优势,遵循最佳实践,编写出高质量的Go语言程序。在Go语言中,接口是一种类型,它定义了一组方法的集合,但不包含这些方法的实现。多态性是面向对象编程的三大特性之一,它指的是允许将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。由于接口类型的变量可以引用实现了该接口的任何类型,因此我们可以实现多态性的效果。

SpencerCharlotte的博客 1027
上一篇: GO语言中的方法
下一篇: go语言中的反射
过去日记
过去日记 新星创作者: 后端开发技术领域 新星创作者: 后端开发技术领域
博客等级 码龄3年 5343粉丝 225原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

过去日记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值