12 go语言(golang) - 数据类型:接口

ops/2024/11/15 0:09:23/

接口

在 Go 语言中,接口(interface)是一种抽象类型,它定义了一组方法的集合,但不包含这些方法的实现。是 Go 语言实现多态和面向对象编程的重要机制之一。

1、接口定义

在 Go 中,接口通过 type 关键字进行定义,并且包含一组方法签名。

type JDBCUtil interface {GetConnection() stringCloseConnection(connection string) string
}

在上面的例子中,JDBCUtil 是一个接口,它要求任何实现该接口的类型都必须有 GetConnection()CloseConnection(connection string) 方法。

2、实现接口

Go 中不需要显式地声明某个类型实现了某个接口,只要该类型提供了与接口所需的方法相同的方法集即可。这被称为隐式实现。

type MysqlUtil struct {url string
}func (m MysqlUtil) GetConnection() string {return "模拟获取连接:" + m.url
}func (_ MysqlUtil) CloseConnection(connection string) string {return fmt.Sprintf("成功关闭连接:【%s】", connection)
}

在这个例子中,MysqlUtil 类型隐式地实现了 JDBCUtil 接口,因为它具有与 JDBCUtil 接口相匹配的方法集。

3、使用接口

可以将具体类型赋值给一个接口变量,从而通过该变量调用其方法。

func Test1(t *testing.T) {util := MysqlUtil{"jdbc://xxx..."}connection := util.GetConnection()fmt.Println(connection)status := util.CloseConnection(connection)fmt.Println(status)
}

输出:

模拟获取连接:jdbc://xxx...
成功关闭连接:【模拟获取连接:jdbc://xxx...】

4、空接口

空接口(interface{})是特殊的,它没有任何方法,因此所有类型都默认实现空接口。这使得空接口非常适合用于存储任意数据。

func Test2(t *testing.T) {var i interface{}i = "abc"fmt.Printf("值:%v,类型:%T \n", i, i)i = 123fmt.Printf("值:%v,类型:%T \n", i, i)
}type AnyType interface {
}func Test3(t *testing.T) {// 推荐简写为:list := []interface{}{1, "a", 3.14, true} ,这样就不用定义AnyType了var list = []AnyType{1, "a", 3.14, true} for _, value := range list {fmt.Printf("值:%v,类型:%T \n", value, value)}
}

输出:

=== RUN   Test2
值:abc,类型:string 
值:123,类型:int === RUN   Test3
值:1,类型:int 
值:a,类型:string 
值:3.14,类型:float64 
值:true,类型:bool 

5、类型断言

当使用空或通用性较强的接口时,你可能需要知道具体存储的数据是什么,这时可以使用类型断言来处理。

func Test4(t *testing.T) {var util = MysqlUtil{"jdbc://xxx..."}var i interface{} = utilif _, ok := i.(JDBCUtil); ok {fmt.Println("JDBCUtil 实现了接口:MysqlUtil")} else {fmt.Println("JDBCUtil 没有实现接口:MysqlUtil")}
}func Test5(t *testing.T) {var i interface{} = MysqlUtil{}switch i.(type) {case string:println("类型是string")case int:println("类型是int")default:println("未知类型")}
}

输出:

=== RUN   Test4
JDBCUtil 实现了接口:MysqlUtil=== RUN   Test5
未知类型

6、多态

多态(Polymorphism)在计算机科学中指的是一种能力,即相同的操作可以作用于不同的数据类型上。在面向对象编程中,多态性通常意味着一个接口可以被多个类型实现,而这些类型可以在不修改调用代码的情况下被使用。

在 Go 语言中,多态性主要通过接口来实现。

多态性的优点

  • 灵活性和可扩展性:你只需确保新添加的类型符合某个既定行为(即,实现了特定的方法集),就能将其无缝地融入现有系统。

  • 代码重用和简化:通过抽象出公共行为,可以避免重复代码,并使得代码更易于维护和理解。

  • 解耦合设计:调用者无需知道具体对象是什么,只需关心它们是否符合所需行为。这种解耦合设计使得系统模块之间更加独立。

练习

在项目中实现注册成功之后向用户发送:邮件、手机的消息提醒。

// 首先,定义一个接口,它包含需要实现的方法。这些方法代表了某种行为或功能。
type Message interface {SendMsg() string
}// 不同的具体类型可以通过实现该接口的方法来表现不同的行为。
type Phone struct {phoneNumber int
}type Email struct {email string
}func (a Phone) SendMsg() string {fmt.Printf("%d,手机用户注册成功\n", a.phoneNumber)return "success"
}func (e Email) SendMsg() string {fmt.Printf("%s,邮箱用户注册成功\n", e.email)return "success"
}// 基于接口的参数,可以实现传入多中类型(多态),也同时具有约束对象必须实现接口方法的功能
func sendMsg(message Message) {message.SendMsg()
}func Test6(t *testing.T) {email := Email{"xxxx@qq.com"}phone := Phone{15800000000}sendMsg(email)sendMsg(phone)
}

输出:

xxxx@qq.com,邮箱用户注册成功
15800000000,手机用户注册成功

底层原理

在 Go 语言中,接口的底层实现是通过一种称为“接口表”(interface table)的机制来实现的。这种机制使得接口能够动态地绑定到具体类型,并支持多态性。

1、空接口的内部结构

type eface struct {_type *_type					// 存储类型相关信息data  unsafe.Pointer	// 存储数据
}
  1. 类型信息(type information):指向一个描述具体类型的结构体,这个结构体包含了该类型的方法集和其他元数据。
  2. 数据指针(data pointer):指向实际存储的数据。如果这个数据是一个值类型,则直接存储;如果是一个引用或指针,则存储的是地址。

2、非空接口的内部结构

type iface struct {tab  *itab         // 指向类型表,它包含了具体类型如何实现该接口的方法信息data unsafe.Pointer // 指向实际数据的指针
}type ITab struct {Inter *InterfaceType // 接口信息,如:接口中定义的方法。Type  *TypeHash  uint32     Fun   [1]uintptr 
}type InterfaceType struct {TypePkgPath NameMethods []Imethod // 接口的方法
}
  1. tab(*itab):这是一个指向 itab 的指针。itab 是一种描述特定类型如何实现某个特定接口的数据结构。它包括了关于该具体类型的方法偏移等信息,以便在运行时能够正确地进行方法调用。是连接特定接口与其实现之间的桥梁。
  2. data(unsafe.Pointer):与 eface 中类似,这是一个不安全指针,用于存储实际的数据地址。如果是值类型,则直接存储其地址;如果是引用或指针,则存储的是该引用或指针所指向内容的地址。
  3. interfacetype:描述一个 Go 接口本身,包括其所属包和需要的方法集。

3、空接口赋值过程

如果在代码中出现其他对象赋值给空接口,其实就是将其他对象相关的值存放到eface的 _typedata中,内部源码:

// The conv and assert functions below do very similar things.
// The convXXX functions are guaranteed by the compiler to succeed.
// The assertXXX functions may fail (either panicking or returning false,
// depending on whether they are 1-result or 2-result).
// The convXXX functions succeed on a nil input, whereas the assertXXX
// functions fail on a nil input.// convT converts a value of type t, which is pointed to by v, to a pointer that can
// be used as the second word of an interface value.
func convT(t *_type, v unsafe.Pointer) unsafe.Pointer {if raceenabled {raceReadObjectPC(t, v, getcallerpc(), abi.FuncPCABIInternal(convT))}if msanenabled {msanread(v, t.Size_)}if asanenabled {asanread(v, t.Size_)}// 使用 mallocgc 为目标类型分配足够大小的堆空间。这个函数会根据传入参数决定是否进行垃圾回收标记。x := mallocgc(t.Size_, t, true)// 使用 typedmemmove 将源地址中的内容复制到新分配的位置。这一步确保了原始数据被正确地移动到新的位置,并且新位置符合目标类型的信息。typedmemmove(t, x, v)// 返回新分配空间的地址,这个地址可以作为接口值中的数据部分使用。return x
}

当你执行类似以下代码时:

var i interface{}
i = someValue

Go 会进行如下操作:

  1. 获取动态类型:查找并获取 someValue 的动态 _type 信息,这包括其内存布局、方法集等元数据信息。
  2. 设置 eface
    • _type*, 即动态类型信息,保存到 eface._type
    • 将实际数据地址保存到 eface.data

思考

1、接口没有强制性的要求我们实现它的方法,跟java不同,那么它的约束性就小了很多,这种情况下还需要使用接口吗?或者说接口的意义是否低了很多?

Go 的接口实现是隐式的,这与 Java 等语言中的显式实现不同,Go 语言的设计哲学之一是“显式优于隐式”,这意味着 Go 语言倾向于让开发者明确地知道他们正在做什么,而不是依赖于编译器来推断或强制实现某些行为。

接口的隐式实现

在 Go 中,类型不需要显式声明实现某个接口。只要类型实现了接口的所有方法,它就自动实现了该接口。这种隐式实现方式减少了样板代码,使得代码更加简洁。

接口的灵活性

由于接口的隐式实现,Go 语言中的接口非常灵活。你可以在不修改现有类型的情况下,通过实现新的接口来扩展类型的功能。这种灵活性使得 Go 语言在处理多种不同类型的场景时非常强大。

接口的多态性

尽管 Go 语言没有类和继承的概念,但接口提供了一种实现多态的方式。你可以定义一个函数,它接受一个接口类型的参数,然后传递任何实现了该接口的类型。这使得你可以编写更加通用和可重用的代码。

golang_328">2、在JAVA中,当我们实现某个接口时,idea会提示我们具体需要实现的所有方法,而golang中没有,那么是不是我们就很容易漏掉需要实现的方法

文档和注释

在定义接口时,可以在文档中详细列出所有方法。这样,当你实现接口时,可以参考这些文档来确保没有遗漏任何方法。

代码审查

代码审查是一个很好的实践,可以帮助发现遗漏的方法实现。同事或团队成员可以检查你的代码,确保你已经实现了接口的所有方法。

测试

编写单元测试来测试你的类型是否正确实现了接口。这不仅可以帮助你发现遗漏的方法,还可以确保你的实现按预期工作。

IDE 插件和工具

使用支持 Go 语言的 IDE(如 GoLand)或集成开发环境(如 Visual Studio Code)时,可以使用一些插件和工具来帮助你识别未实现的接口方法。例如,GoLand 有一个功能,可以在你实现接口时自动提示你需要实现的方法。


http://www.ppmy.cn/ops/133712.html

相关文章

pytorch3d导入maya相机位姿踩坑

目的是将maya中的相机无缝导入到pytorch3d中 坑1: 直接导出maya中相机的欧拉角以及Trans, 然后直接导入pytorch3d中 问题所在: maya中直接导出的相机旋转角以及Trans是 c2w, 而根据文档https://pytorch3d.org/docs/cameras 中的一句话, 经过R可以实现world to view的变换, 所…

云服务器和物理服务器的区别有哪些?

云服务器是服务器集群虚拟为多个性能可配的虚拟主机,云服务器的基础依旧是需要物理服务器的,要用海量的物理服务器集群进行建构,以此来形成云端虚拟资源池,根据企业的自身需求,来从资源池中进行资源分配。 物理服务器则…

3DTiles之i3dm介绍

3DTiles之i3dm介绍 3D Tiles 是一种用于高效存储和传输三维城市、建筑、地形、点云等空间数据的开放标准格式。i3dm(Intel 3D Model)是 3D Tiles 中用于表示三维模型(如建筑物或其他对象)的一个子格式。i3dm 格式的出现&#xff…

MongoDB在现代Web开发中的应用

💓 博客主页:瑕疵的CSDN主页 📝 Gitee主页:瑕疵的gitee主页 ⏩ 文章专栏:《热点资讯》 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 MongoDB在现代Web开发中的应用 引言 MongoDB 概述 定义与原理 发展…

Sqoop学习

目录 一、Soop简介 二、Sqoop的安装 1. 上传压缩包到/opt/install目录下 2.解压 3.修改文件名 4.拷贝sqoop-1.4.7.bin__hadoop-2.6.0目录下的sqoop-1.4.7.jar包到/opt/soft/sqoop147目录下 5.拷贝sqoop-1.4.7.bin__hadoop-2.6.0/lib目录下该jar包到sqoop/lib目录下 6.复…

SOLIDWORKS代理商鑫辰信息科技

鑫辰信息科技是一家专业的SOLIDWORKS代理商,致力于为客户提供全面的CAD解决方案和技术支持。作为SOLIDWORKS的授权合作伙伴,鑫辰信息科技在机械设计、产品开发和工程仿真等领域拥有丰富的经验和专业知识,能够帮助客户有效提升设计和生产效率。…

Go 语言已立足主流,编程语言排行榜24 年 11 月

Go语言概述 Go语言,简称Golang,是由Google的Robert Griesemer、Rob Pike和Ken Thompson在2007年设计,并于2009年11月正式宣布推出的静态类型、编译型开源编程语言。Go语言以其提高编程效率、软件构建速度和运行时性能的设计目标,…

【Python】爬虫通过验证码

1、将验证码下载至本地 # 获取验证码界面html url http://www.example.com/a.html resp requests.get(url) soup BeautifulSoup(resp.content.decode(UTF-8), html.parser)#找到验证码图片标签,获取其地址 src soup.select_one(div.captcha-row img)[src]# 验证…