golang中的Interface接口 类型断言、接口赋值、空接口的使用、接口嵌套
Interface整理
文章目录
- Interface整理
- 接口嵌套接口
- 类型断言
- 类型判断 type-switch
- 使用方法集与接口
- 空接口
- 实例
- 接口赋值给接口
接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。接口彻底将类型能做什么,以及如何做分离开来,使得相同接口的变量在不同的时刻表现出不同的行为,这就是多态的本质。
编写参数是接口变量的函数,这使得它们更具有一般性。
使用接口使代码更具有普适性。
最近在学Go当中的接口,学的有点云里雾里 ,这个interface和Java的也太不像了,我们先来看看Java当中的接口是怎么用的:
首先我们先定义一个接口:
public interface Study { //使用interface表示这是一个接口void study(); //接口中只能定义访问权限为public抽象方法,其中public和abstract关键字可以省略
}
之后我们用关键字继承:
public class Student extends Person implements Study { //使用implements关键字来实现接口public Student(String name, int age, String sex) {super(name, age, sex, "学生");}@Overridepublic void study() { //实现接口时,同样需要将接口中所有的抽象方法全部实现System.out.println("我会学习!");}
}public class Teacher extends Person implements Study {protected Teacher(String name, int age, String sex) {super(name, age, sex, "教师");}@Overridepublic void study() {System.out.println("我会加倍学习!");}
这样一个显示继承的方式非常清晰明了,接下来看看Go里面的接口:
type Namer interface {Method1(param_list) return_typeMethod2(param_list) return_type...
}
这样一看没有什么很大的区别,都需要先声明一个接口但是不使用,接下来看看接口的实现:
package mainimport "fmt"type Shaper interface {Area() float32
}type Square struct {side float32
}func (sq *Square) Area() float32 {return sq.side * sq.side
}func main() {sq1 := new(Square)sq1.side = 5var areaIntf ShaperareaIntf = sq1// shorter,without separate declaration:// areaIntf := Shaper(sq1)// or even:// areaIntf := sq1fmt.Printf("The square has area: %f\n", areaIntf.Area())
}
这样就会发现如下几个区别:
- 并没有显式继承
- 接口能声明变量,并通过该变量指向方法
- 实现方法中的参数为自定义的结构体
一个接口类型的变量或一个 接口值 :
首先我们来看第一点,关于为什么不显示继承,这一点我在网上搜过,观点基本是Go强调的是组合而非继承,并没有一个很确切的理论,那暂且不议
第二点:areaIntf
是一个多字(multiword)数据结构,它的值是 nil
。接口变量里包含了接收者实例的值和指向对应方法表的指针。
在Go中,我们自定义的结构体就像Java中的类一样,可以实现接口中的方法。我们可以同一个接口被实现多次。当时就有了点疑问:不是不允许函数重载吗?后来发现方法和函数是完全不同的概念:
Go中不允许函数(function)重载是为了提高效率,而方法(method)的可多次实现则体现了Go的多态,也就是根据场景选择。
接下来,我们看一些进阶功能:
接口嵌套接口
在Java 和go当中,我们都倡导一个接口的简洁明了。比如说先定义一个结构体为综测,综测又是由考试成绩、竞赛、体育等等组成,考试成绩里面又有不同科,体育里面也有不同科,这个时候我们就应该分开定义,之后进行嵌套。我个人的理解的理解就是类似于树一样的存在,而一个结构体就是一个父节点。这里还是放一个实例:
type ReadSeeker interface {ReaderSeeker
}type Reader interface {Read(p []byte) (n int, err error)
}type Seeker interface {Seek(offset int64, whence int) (int64, error)
}
类型断言
我们通常会想知道一个接口变量里面是什么类型,这个时候我们就会用到类型断言,通用格式为:
typeA := var1.(T)
var1为接口变量,T是想知道的类型。如果转换合法,typeA
是 var1
转换到类型 T
的值
如果在判断式中使用,则是这样的:
if t, ok := areaIntf.(*Square); ok {fmt.Printf("The type of areaIntf is: %T\n", t)
}
如果转换合法,t
是 转换到类型的值,ok
会是 true
;否则 t
是类型的零值,ok
是 false
,也没有运行时错误发生。
注意:如果忽略 areaIntf.(*Square)
中的 *
号,会导致编译错误:impossible type assertion: Square does not implement Shaper (Area method has pointer receiver)
。
同理,我们也可以判断他是否属于该接口:
type Stringer interface {String() string
}if sv, ok := v.(Stringer); ok {fmt.Printf("v implements String(): %s\n", sv.String()) // note: sv, not v
}
类型判断 type-switch
个人认为如果说类型断言是只想知道值是不是某个类型,那么此语句则是想知道究竟是哪个重要的类型或者不需要知道的类型,常见用法如下:
func classifier(items ...interface{}) {for i, x := range items {switch x.(type) {case bool:fmt.Printf("Param #%d is a bool\n", i)case float64:fmt.Printf("Param #%d is a float64\n", i)case int, int64:fmt.Printf("Param #%d is a int\n", i)case nil:fmt.Printf("Param #%d is a nil\n", i)case string:fmt.Printf("Param #%d is a string\n", i)default:fmt.Printf("Param #%d is unknown\n", i)}}
}
可以用 type-switch
进行运行时类型分析,但是在 type-switch
不允许有 fallthrough
。
使用方法集与接口
作用于变量上的方法实际上是不区分变量到底是指针还是值的。当碰到接口类型值时,这会变得有点复杂,原因是接口变量中存储的具体值是不可寻址的,
package mainimport ("fmt"
)type List []intfunc (l List) Len() int {return len(l)
}func (l *List) Append(val int) {*l = append(*l, val)
}type Appender interface {Append(int)
}func CountInto(a Appender, start, end int) {for i := start; i <= end; i++ {a.Append(i)}
}type Lener interface {Len() int
}func LongEnough(l Lener) bool {return l.Len()*10 > 42
}func main() {// A bare valuevar lst List// compiler error:// cannot use lst (type List) as type Appender in argument to CountInto:// List does not implement Appender (Append method has pointer receiver)CountInto(lst, 1, 10) //错误代码 if LongEnough(lst) { // VALID: Identical receiver typefmt.Printf("- lst is long enough\n")}// A pointer valueplst := new(List)CountInto(plst, 1, 10) // VALID: Identical receiver typeif LongEnough(plst) {// VALID: a *List can be dereferenced for the receiverfmt.Printf("- plst is long enough\n")}
}
输出
讨论
在 lst
上调用 CountInto
时会导致一个编译器错误,因为 CountInto
需要一个 Appender
,而它的方法 Append
只定义在指针上。 在 lst
上调用 LongEnough
是可以的,因为 Len
定义在值上。
在 plst
上调用 CountInto
是可以的,因为 CountInto
需要一个 Appender
,并且它的方法 Append
定义在指针上。 在 plst
上调用 LongEnough
也是可以的,因为指针会被自动解引用。
总结
在接口上调用方法时,必须有和方法定义时相同的接收者类型或者是可以根据具体类型 P
直接辨识的:
- 指针方法可以通过指针调用
- 值方法可以通过值调用
- 接收者是值的方法可以通过指针调用,因为指针会首先被解引用
- 接收者是指针的方法不可以通过值调用,因为存储在接口中的值没有地址
将一个值赋值给一个接口时,编译器会确保所有可能的接口方法都可以在此值上被调用,因此不正确的赋值在编译期就会失败。
译注
Go 语言规范定义了接口方法集的调用规则:
- 类型
T
的可调用方法集包含接受者为T
或T
的所有方法集 - 类型
T
的可调用方法集包含接受者为T
的所有方法 - 类型
T
的可调用方法集不包含接受者为T
的方法
接下来我们讨论下空接口
空接口
定义:不包含任何方法,对实现没有要求
空接口类似 Java/C#
中所有类的基类: Object
类,二者的目标也很相近。
可以给一个空接口类型的变量 var val interface {}
赋任何类型的值
每个 interface {}
变量在内存中占据两个字长:一个用来存储它包含的类型,另一个用来存储它包含的数据或者指向数据的指针。
这样光看似乎觉得没什么大不了的,我们举个例子,比如说创建树或者其他数据结构,如果我们要根据每个数据类型来定义不同的方法,那无疑是很浪费时间的,这时候就可以用到空接口,实现一键通用:
package mainimport ("fmt"
)type Node struct {le *Nodedata interface{}rl *Node
}func NewNode(left, right *Node) *Node {return &Node{left, nil, right}
}func (n *Node) setData(data interface{}) {n.data = data
}func main() {root := NewNode(nil, nil)root.setData("root node")a := NewNode(nil, nil)a.setData("left node")b := NewNode(nil, nil)b.setData(1)root.le = aroot.rl = bfmt.Printf("%v\n", root)
}
实例
我们来看一些实际应用,在GORM框架中,我们创建对象可以使用map的数据结构导入,但是我们无法保证数据都是一个类型,所以就需要一个空接口来帮我们接住所有类型:
db.Model(&User{}).Create([]map[string]interface{}{{"Name": "jinzhu_1", "Age": 18},{"Name": "jinzhu_2", "Age": 20},
})
接口赋值给接口
一个接口的值可以赋值给另一个接口变量,前提是底层类型实现了必要的方法,此转换是在运行时检查的,转换失败的时候会导致一个运行时错误,这也是GO的动态的一点
比如此代码
package mainimport "fmt"type Shaper interface {Area() float64
}type Square struct {side float64
}func (s Square) Area() float64 {return s.side * s.side
}type Circle struct {radius float64
}func main() {var s Shaperc := Circle{radius: 5.0}// 错误的示例:将接口 Shaper 赋值给接口 Shaper,但底层类型 Circle 并没有实现 Area() 方法s = cfmt.Printf("Area of the shape: %f\n", s.Area())
}
错误显示:
相关文章:

golang中的Interface接口 类型断言、接口赋值、空接口的使用、接口嵌套
Interface整理 文章目录 Interface整理接口嵌套接口类型断言类型判断 type-switch使用方法集与接口空接口实例 接口赋值给接口 接口是一种契约,实现类型必须满足它,它描述了类型的行为,规定类型可以做什么。接口彻底将类型能做什么࿰…...
使用设计模式省去大量的if-elsef分支
1.测试类 Testpublic void test7() {/*** 使用设计模式前*///模拟入参String name "?";if("张三".equals(name)){System.out.println("按照张三的策略执行的任务!");}else if ("李四".equals(name)){System.out.println("按照李…...

Tomcat安装与配置文件解读
简介 Tomcat是Apache软件基金会(Apache Software Foundation)项目中的一个核心项目,由Apache、Sun和其他一些公司及个人共同开发而成。 Tomcat服务器是一个免费的开放源代码的Web应用服务器,属于轻量级应用服务器,在…...

计算机网络重点概念整理-第一章 计算机网络概述【期末复习|考研复习】
计算机网络复习系列文章传送门: 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 前言一、计算机网络概述1.1 计算机网络的定义:1.2 计算机网…...

Day 11 python学习笔记
模块 内置模块 random random:随机数模块 我们可以在解释器中看到其蕴含的方法 接下来我解释一些常用的方法: random.random( ) random.random( ) 返回0-1的随机数 [0,1) >>> random.random() 0.364183511476754 random.randint(n,m) r…...

HarmonyOS鸿蒙原生应用开发设计- 图标库
HarmonyOS设计文档中,为大家提供了独特的图标库,开发者可以根据需要直接引用。 图标库可以分为双色图标、填充图标、线性图标。具体分为 键盘、箭头、连接状态、媒体、人、设备、索引、通信、文件、物体与工具等。 整体分类 开发者直接使用官方提供的图标…...

微软bing大声朗读文档或网页卡顿老是中断,用离线的huihui就很流畅但没那么自然
默认的xiaoxiao_online好听,但卡顿,朗读功能确实受到了网络状态的影响。 大概率是网络问题。...

Java VMTranslator Part I
目录 堆栈运算命令 基本思路 核心代码 Parser Code Writer Main 实验结果,使用SimpleAdd、StackTest进行验证 内存访问命令 基本思路 核心代码 Parser Code Writer Main 实验结果,使用进行验证。对比生成的二进制代码文件。 用Java写一个翻…...
ES6带来那些js新特性?
ECMAScript 6(ES6),也称为 ECMAScript 2015,引入了许多重大的改进和新特性,以改善JavaScript语言的功能和可读性。以下是一些ES6中的主要改变和新特性: 1、let 和 const 声明: 引入了 let 和 const 关键字…...
js数组深拷贝汇总
1.for 循环实现数组的深拷贝 通过对数组的for循环,即可实现对数组的深拷贝了。 var arr [1,2,3,4,5] var arr2 copyArr(arr) function copyArr(arr) {let res []for (let i 0; i < arr.length; i) {res.push(arr[i])}return res }2.slice 方法实现数组的深…...

错误 LNK1112 模块计算机类型“x64”与目标计算机类型“X86”冲突
这个错误表明你在进行链接时,模块的计算机类型与目标计算机类型冲突。 在这里,“x64”代表64位系统,“X86”代表32位系统。 要解决这个问题,你需要确保你的所有模块(包括库文件和依赖项)都是与你的目标计…...

java八股文(基础篇)
面向过程和面向对象的区别 面向过程:在解决问题时,特别自定义函数编写一步一步的步骤解决问题。 面向对象:其特点就是 继承,多态,继承,在解决问题时,不再注重函数的编写,而在于注重…...

window系统修改rabbitmq 默认端口
安装完rabbitmq之后,默认的client端口是5672, 控制台访问端口是15672,rabbitmq管理工具启动之后在浏览器中输入地址: http://localhost:15672/ 就可以访问后台 , 默认管理员账号:guest 密码&#x…...

七人拼团模式:颠覆你的购物观念,499元产品让你赚翻天!
七人拼团模式是一种创新的消费模式,通过聚集消费者的购买力,让消费者能够以更优惠的价格购买到优质的商品。下面我们以499元的产品为例,详细介绍七人拼团模式的玩法规则和收益计算。 玩法规则: 消费者购买499元的指定产品后&…...

【机器学习合集】模型设计之卷积核设计 ->(个人学习记录笔记)
文章目录 卷积核设计1. 基于参数压缩的卷积设计1.1 【11卷积】1.2 【11卷积典型应用】1.3 【小卷积的使用】 2. 基于感受野的卷积设计2.1 膨胀卷积(带孔卷积,strous convolution)2.2 可变形卷积2.3 非局部卷积 3. 基于卷积操作的优化3.1 移位网络3.2 加法网络 卷积核…...
JS实现用户二次确认后再提交表单
HTML代码 <form id"importForm" action"" method"post" enctype"multipart/form-data" onsubmit"return confirmSubmit()"> ...... <input id"btnImportSubmit" class"btn btn-primary" type…...

1992-2021年全国各省经过矫正的夜间灯光数据(GNLD、VIIRS)
1992-2021年省市县经过矫正的夜间灯光数据(GNLD、VIIRS) 1、时间:1992-2021年3月,其中1992-2013年为年度数据,2013-2021年3月为月度数据 2、来源:DMSP、VIIRS 3、范围:31省 4、指标解释&…...

JMeter的使用——傻瓜式学习【中】
目录 前言 1、JMeter参数化 1.1、什么是参数化 1.2、用户定义的变量 1.2.1、什么时候使用用户定义的变量 1.2.2、使用“用户定义的变量”进行参数化的步骤: 1.2.3、案例 1.3、用户参数 1.3.1、什么时候使用用户参数? 1.3.2、使用“用户参数”进…...

MyBaties存储和查询json格式的数据(实体存储查询版本)
最近在做的功能,由于别的数据库有值,需要这边的不同入口的进来查询,所以需要同步过来,如果再继续一个一个生成列对应处理感觉不方便,如果没有别的操作,只是存储和查询,那就可以用MySql支持的jso…...

动态规划14:一和零
动态规划14:一和零 题目 474. 一和零 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集的长度,该子集中 最多 有 m 个 0 和 n 个 1 。 如果 x 的所有元素也是 y 的元素,集合 x 是集合 y 的 子集 。 …...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...

解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...

基于单片机的宠物屋智能系统设计与实现(论文+源码)
本设计基于单片机的宠物屋智能系统核心是实现对宠物生活环境及状态的智能管理。系统以单片机为中枢,连接红外测温传感器,可实时精准捕捉宠物体温变化,以便及时发现健康异常;水位检测传感器时刻监测饮用水余量,防止宠物…...