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

接口

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

1、接口定义

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

type JDBCUtil interface {
	GetConnection() string
	CloseConnection(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 = 123
	fmt.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{} = util

	if _, 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  *Type
	Hash  uint32     
	Fun   [1]uintptr 
}

type InterfaceType struct {
	Type
	PkgPath Name
	Methods []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 语言没有类和继承的概念,但接口提供了一种实现多态的方式。你可以定义一个函数,它接受一个接口类型的参数,然后传递任何实现了该接口的类型。这使得你可以编写更加通用和可重用的代码。

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

文档和注释

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

代码审查

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

测试

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

IDE 插件和工具

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/915832.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

10款PDF合并工具讲解与推荐!!!

在现在的大环境下,PDF文件因其跨平台、格式固定等优势,成为了我们工作和学习中不可或缺的一部分。是最常用的文档格式之一。然而,面对多个PDF文件需要合并成一个的场景,如何选择一款高效、易用的PDF合并工具就显得尤为重要。今天&…

「QT」窗口类 之 QWidget 窗口基类

✨博客主页何曾参静谧的博客📌文章专栏「QT」QT5程序设计📚全部专栏「Win」Windows程序设计「IDE」集成开发环境「UG/NX」BlockUI集合「C/C」C/C程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「UG/NX」NX定制…

【机器学习】如何配置anaconda环境(无脑版)

马上就要上机器学习的实验,这里想写一下我配置机器学习的anaconda环境的二三事 一、首先,下载安装包: Download Now | Anaconda 二、打开安装包,一直点NEXT进行安装 这里要记住你要下载安装的路径在哪,后续配置环境…

【3D Slicer】的小白入门使用指南四

开源解剖影像浏览工具Open Anatomy Browser使用及介绍 和3D slicer米有太大关系,该工具是网页版影像数据的浏览工具(可以简单理解为网页版的3D slicer) 介绍 ● 开放解剖(OA)浏览器是由神经影像分析中心开发的,基于网络浏览器技术构建的图谱查看器。 ● OA浏览器将解剖模…

Unity使用PS合并贴图

前言 使用PBR渲染,金属工作流时,默认使用一个金属度贴图,其中r通道保存金属度,a通道保存光滑度,g通道和b通道没使用; 我们很可能使用Occlusion Map,使用其中的g通道保存Occlusion 信息。单独使用…

Linux中.NET读取excel组件,不会出现The type initializer for ‘Gdip‘ threw an exception异常

组件,可通过nuget安装,直接搜名字: ExcelDataReader using ConsoleAppReadFileData.Model; using ExcelDataReader; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Task…

Qt_day4_Qt_UI设计

目录 Qt_UI设计 1. Designer 设计师(掌握) 2. Layout 布局(重点) 2.1 基本使用 2.2 高级用法 2.3 代码布局(了解) 3. Designer与C的关系(熟悉) 4. 基本组件(掌握…

Axure网络短剧APP端原型图,竖屏微剧视频模版40页

作品概况 页面数量:共 40 页 使用软件:Axure RP 9 及以上,非软件无源码 适用领域:短剧、微短剧、竖屏视频 作品特色 本作品为网络短剧APP的Axure原型设计图,定位属于免费短剧软件,类似红果短剧、河马剧场…

网安加·百家讲坛 | 仝辉:金融机构鸿蒙应用安全合规建设方案

作者简介:仝辉,北京娜迦信息科技发展有限公司攻防安全负责人,深耕移动应用安全领域十余年,获得过CISP、CISSP、OSCP、PMP、CCRC-CIASW等相关证书,参与多项移动应用安全标准起草,参与华为、平安集团、中国移…

C语言 strlen 函数 - C语言零基础入门教程

目录 一.strlen 函数简介二.strlen 函数实战三.猜你喜欢 零基础 C/C 学习路线推荐 : C/C 学习目录 >> C 语言基础入门 一.strlen 函数简介 在C 语言中,char 字符串也是一种非常重要的数据类型,我们可以使用 strlen 函数获取字符串长度;…

OceanStor Pacific系列 8.1.0 功能架构

功能架构 华为OceanStor Pacific系列提供基于三层的分布式存储架构,融合分布式文件、对象、大数据和块多个服务形态,支持文件、对象、大数据服务部署在一个集群,并统一管理。 华为OceanStor Pacific系列整体功能架构由存储接口层、存储服务…

Simulink中Matlab function使用全局变量

目录 一. 引言二. 普通Matlab function使用全局变量三. Simulink中的Matlab function使用全局变量四. 如何利用Matlab function的全局变量施加随机噪声 一. 引言 最近发现了之前仿真中的一个问题,记录一下备忘。 Matlab function中有时候需要用到全局变量&#xf…

react-markdown内容宽度溢出和换行不生效问题

情景复现: 解决办法,添加样式进行限制 /* index.css */ .markdown-container {word-break: break-word; /* 强制长单词断行 */white-space: pre-wrap; /* 保留空白符序列,但是正常地进行换行 */overflow-wrap: break-word; /* 在长单词或…

【C#设计模式(10)——装饰器模式(Decorator Pattern)】

前言 装饰器模式可以在运行时为对象添加额外的功,而无需修改原始对象的代码。这种方式比继承更加灵活。 代码 //蛋糕类(抽象类) public abstract class Cake {public abstract void Create(); } //奶油蛋糕类 public class CreamCake : Cak…

Diffusion Transformer模型结构解析(DiT、SD3、Flux)

Diffusion Transformer模型结构解析(DiT、SD3、Flux) 本文将通过 DiT、SD3、Flux 三个 DiT 相关工作,介绍 Diffusion 中的 Transformer 结构的应用与演进。注意 SD3 和 Flux 采用的 Flow Matching 的扩散模型形式化当然是很关键的改进&#…

给阿里云OSS绑定域名并启用SSL

为什么要这么做? 问题描述: 当用户通过 OSS 域名访问文件时,OSS 会在响应头中增加 Content-Disposition: attachment 和 x-oss-force-download: true,导致文件被强制下载而不是预览。这个问题特别影响在 2022/10/09 之后新开通 OS…

如何找到系统中bert-base-uncased默认安装位置

问题: 服务器中无法连接huggingface,故需要自己将模型文件上传 ubuntu 可以按照这个链接下载 Bert下载和使用(以bert-base-uncased为例) - 会自愈的哈士奇 - 博客园 里面提供了giehub里面的链接 GitHub - google-research/be…

【算法】区间DP

基本内容 [!NOte] 通过分治的思想实现DP数组 入门例子 NOI1995] 石子合并 - 洛谷 | 计算机科学教育新生态 题目要求:给定一圈石头数组,每个石头对应一个权重值,当两个石头合并时组成一个小石头堆,成本为两个石头权重值相加&#x…

机器学习—决定下一步做什么

现在已经看到了很多不同的学习算法,包括线性回归、逻辑回归甚至深度学习或神经网络。 关于如何构建机器学习系统的一些建议 假设你已经实现了正则化线性回归来预测房价,所以你有通常的学习算法的成本函数平方误差加上这个正则化项,但是如果…

第二十周机器学习笔记:初步认识PINN

第二十周周报 摘要Abstract一、初步认识物理信息神经网络(PINN)1.PINN的基本概念2. PINN与传统机器学习的区别3.构建PINN的步骤 二、代码实战——比较RNN、LSTM、Transformer在股市预测的表现1.RNN在股市预测中的表现2.LSTM在股市预测中的表现3.Transfor…