学过Java的同学都知道在Java中接口更像是一种规范,用接口定义了一组方法,下面实现这个接口的类只管按照写好的方法名和返回值去实现就好,内部如何实现是各个方法自己的事情,接口本身不关注。

另外Java中实现接口的类必须显式的声明实现了哪个接口: implement InterfaceName,仔细思考一下会有如下问题:

  1. 如果你修改了接口名,那么类也得跟着修改;
  2. 你必须先定义接口,才能去实现它;
  3. 一个类可以实现多个接口,Java中接口的设计更像是弥补继承的不足,如果你希望实现类再实现一个接口,那么的继续修改实现类。

带着以上几个问题,我们先看Go中如何去使用Interface,再去探讨理论的问题。

1.定义interface

在Go中接口是用type关键字定义,所以可以说interface是一种具有一组方法的类型,这些方法定义了interface的行为。当然,更突出的是Go允许不带任何方法的interface,这种类型的interface叫做empty interface

下面的例子展示一个结构体实现了两个接口,但是一眼看上去你无法得知他实现了哪两个接口。

type Person interface {
GetAge() int
GetName() string
} type Car interface {
GetAge() int
GetName() string
} type Student struct {
age int
name string
} func (s Student) GetAge() int {
return s.age
} func (s Student) GetName() string {
return s.name
}

上面有两个接口Person,Car。Stuent结构体有两个方法和这两个结构体中的方法一样,这就表示Student实现两这两个接口,当然你可以试一下将其中的一个方法注释掉。如果你用的GoLand 编辑器,你会发现编辑器左边的Student身上带着的上限的绿色箭头消失了,表示当前结构体没有实现任何方法。

综上:接口中有几个方法,实现者必须完全实现这些方法才表示当前实现的结构体或者自定义类型实现了这个接口。这一点跟Java中并无区别。

另外你是否也发现,其实你可以先去实现方法,在定义接口,因为接口跟实现之间完全没有耦合关系,接口是根据适用方的需求来定义的,完全不必要关心是否有其他地方定义过。

2.使用interface

先来看如下一段代码:

package main

import "fmt"

type Get interface {
Get_name() string
} type Person struct {
age int16
name string
} type Student struct {
no int16
name string
} func (p Person) Get_name() string {
return p.name
} func (s Student) Get_name() string {
return s.name
} func Get_name(get Get) string {
return get.Get_name()
} func main() {
s := Student{12345,"xiaoming"}
name := Get_name(s)
fmt.Println(name)
}

上面定义了一个接口Get,分别定义了两个结构体都去实现了这个接口,那么如何去通过接口帮我们隐藏当前调用的哪个具体的实现呢,关键就在Get_name方法,这个方法的参数是接口本身,里面的实现你可以通过接口去调用他的任何方法,这些调用目前为止跟任何实现没有任何关系。

注意到下面我们在main方法中定义了一个Student类型的对象s,然后将s传入Get_name方法中,这就表示当前接口的实现者是Student,那么他走的就是Student相关的实现逻辑。即当前Get接口类型的变量get存储的是一个Student对象。

3. 如何判断 interface 变量存储的是哪种类型

现实开发中可能会有一种需求:因为Get_name是每个实现者自己去实现的方法,那么如果想在Get_name方法外面加点料,即需要判断当前来的实现者是哪种类型然后针对某种类型的实现者加点料,该如何做呢。

func Get_name(get Get) string {
switch get.(type) {
case Person:
fmt.Println("person")
case Student:
fmt.Println("student")
}
return get.Get_name()
}

注意:interface.(type)方法只能在switch中实现,出了switch方法你就不能用.的方式调出来这个方法了。

.(type)表示当前我们要断言的类型。

4. 空的interface

interface{} 是一个空的 interface 类型,根据前文的定义:一个类型如果实现了一个 interface 的所有方法就说该类型实现了这个 interface,空的 interface 没有方法,所以可以认为所有的类型都实现了 interface{}。如果定义一个函数参数是 interface{} 类型,这个函数应该可以接受任何类型作为它的参数。

func doAnything(i interface{}){

}

这是一个很重要的特性。

4.1 使用空interface作为返回参数接收任一类型返回值

我们来看下面这个例子:

type BaseInfo struct{
userId int
userName string
} type StudentInfo struct{
BaseInfo
Options []string
} type StaffInfo struct{
StudentInfo
cardId string
} func checkData(id int) (interface{} , bool) {
data1 ,ok1 := CheckStaffData(id) // 检查员工信息是否正确,返回StaffInfo
data2 ,ok2 := CheckStudentData(id) // 检查学生信息是否正确,返回StudentInfo if ok1 {
return data1,ok1
} if ok2 {
return data2,ok2
} return nil ,false
}

checkData方法中我们使用了接口作为第一个返回值的接收参数。任何类型都可以。那么下一个问题来了,我们如何去解析用接口接收的这个返回吹呢?还记得上面用 switch interface.(type)判断的例子吧,所以代码可以这样写:

func getData()  {
if data,ok := fetchQuestion(22); ok {
switch data.(type) {
case StaffInfo:
fmt.Println("staffInfo")
case StudentInfo:
fmt.Println("student")
case A:
fmt.Println("A")
case B:
fmt.Println("B")
...
...
...
} } }

有没有发现这个switch很有可能会成为巨无霸。

在Java中是如何解决这个问题的呢?Java提供了继承和接口的方法,通过定义一个抽象类,或者一个接口,让每一个case的逻辑去实现这个抽象类或者接口,使用工厂模式去加载需要的实现类即可。每次有变动我们只用去更改工厂类,新增新的实现,getData方法可以不用变动。

那么在Go中如何实现呢?


type InfoType interface {
getType() int
getData(s string)
} type BaseInfo struct{
userId int
userName string
returnType int8
} type StudentInfo struct{
BaseInfo
Options []string
} type StaffInfo struct{
StudentInfo
cardId string
} func (b BaseInfo) getType() int8 {
return b.returnType
} func (b BaseInfo) getData(s string) {
b.getData(s)
} func fetchQuestion(id int) (InfoType , bool) {
data1 ,ok1 := CheckStaffData(id) // 检查员工信息是否正确,返回StaffInfo
data2 ,ok2 := CheckStudentData(id) // 检查学生信息是否正确,返回StudentInfo if ok1 {
return data1,ok1
} if ok2 {
return data2,ok2
} return nil ,false
} func getData() {
var s string
if data,ok := fetchQuestion(22); ok {
switch data.getType() {
case A:
s = A
case B:
s = B
case C:
s = C
}
data.getData(s)
} }

在上面代码中,把人员类型抽象成了一个接口,定义了两个方法:

  • 获取类型;
  • 根据类型选择不同的实现;

BaseInfo和StudentInfo可以分别去实现这个接口。

最关键的是getData方法的修改,case中只用对type赋值即可,最后由getData方法根据type去调用不同的实现。简化了操作。

5. []interface{}的使用

上面刚说interface{}作为参数陈可以承接任何类型,那么[]interface{}作为参数是不是也可以承接任何类型的数组呢?先来试验一下。

package main

import (
"fmt"
) func getAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
} func main() {
names := []string{"a", "b", "b"}
getAll(names)
}

如果你用的是GoLand,你会发现检查就报错,还没到编译期。

interface{}可以表示任一类型,不表示[]interface{}可以表示任意类型的数组,它只是表示这是一个数组,里面你想装啥都可以。

正确的使用[]interface{}的姿势是这样的,你需要new 一个interface类型的数组对象,将想要的元素塞进这个数组就可以。

package main

import (
"fmt"
) func getAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
} func main() {
names := []string{"a", "b", "b"}
c := 3
v := make([]interface{},4)
for i,value :=range names {
v[i] = value
}
v[3] = c
getAll(v) fmt.Println(v)
}

Go中的interface学习的更多相关文章

  1. SpringBoot中JPA的学习

    SpringBoot中JPA的学习 准备环境和项目配置 写一下学习JPA的过程,主要是结合之前SpringBoot + Vue的项目和网上的博客学习一下. 首先,需要配置一下maven文件,有这么两个 ...

  2. iOS中,在类的源文件(.m)中,@interface部分的作用?

      此@interface部分为类扩展(extension). 其被设计出来就是为了解决两个问题的 其一,定义类私有方法的地方. 其二,实现public readonly,private readwr ...

  3. PHP中的Libevent学习

    wangbin@2012,1,3 目录 Libevent在php中的应用学习 1.      Libevent介绍 2.      为什么要学习libevent 3.      Php libeven ...

  4. OC中的@interface和java中的区别以及 @implementation @protocol

      java 在java中的interface是‘接口’的意思,而java的类声明用class,即接口用interface声明,类是用class声明,是两个独立的部分. 只有在类声明要实现某个接口时, ...

  5. JS中childNodes深入学习

    原文:JS中childNodes深入学习 <html xmlns="http://www.w3.org/1999/xhtml"> <head> <ti ...

  6. CNCC2017中的深度学习与跨媒体智能

    CNCC2017中的深度学习与跨媒体智能 转载请注明作者:梦里茶 目录 机器学习与跨媒体智能 传统方法与深度学习 图像分割 小数据集下的深度学习 语音前沿技术 生成模型 基于贝叶斯的视觉信息编解码 珠 ...

  7. 图解BERT(NLP中的迁移学习)

    目录 一.例子:句子分类 二.模型架构 模型的输入 模型的输出 三.与卷积网络并行 四.嵌入表示的新时代 回顾一下词嵌入 ELMo: 语境的重要性 五.ULM-FiT:搞懂NLP中的迁移学习 六.Tr ...

  8. python中confIgparser模块学习

    python中configparser模块学习 ConfigParser模块在python中用来读取配置文件,配置文件的格式跟windows下的ini配置文件相似,可以包含一个或多个节(section ...

  9. 【荐】详解 golang 中的 interface 和 nil

    golang 的 nil 在概念上和其它语言的 null.None.nil.NULL一样,都指代零值或空值.nil 是预先说明的标识符,也即通常意义上的关键字.在 golang 中,nil 只能赋值给 ...

随机推荐

  1. pod update更新error: RPC failed; curl 18 transfer closed with outstanding read data remaining

    1. pod update 的时候出现下边的错误 error: RPC failed; curl 18 transfer closed with outstanding read data remai ...

  2. 50行Python代码,教你获取公众号全部文章

    > 本文首发自公众号:python3xxx 爬取公众号的方式常见的有两种 - 通过搜狗搜索去获取,缺点是只能获取最新的十条推送文章 - 通过微信公众号的素材管理,获取公众号文章.缺点是需要申请自 ...

  3. 视图系统CBV 和 response

     CBV和FBV FBV(function based view ) CBV(class based view) 1. CBV的定义 # 增加出版社 CBV from django.views imp ...

  4. S7 300数据块中的变量指定断电保持特性

    利用DB块内变量属性的保持性,勾选上就可以保持了.300PLC数据块默认断电保持.

  5. Oracle数据库---触发器

    SQL> --当我们对empnew执行删除操作之后,它就会出现一个提示信息,提示:这是删除操作!SQL> CREATE TRIGGER first_trigger 2 AFTER DELE ...

  6. C语言学习书籍推荐《C程序设计语言(第2版•新版)》下载

    克尼汉 (作者), 等 (作者, 译者), 徐宝文 (译者) 下载地址:点我 <C程序设计语言(第2版•新版)>是由C语言的设计者Brian W.Kernighan和Dennis M.Ri ...

  7. 赛前集训的第二个小总结(OI生涯盛极必衰orNOIP前最后试炼?)+关于学OI目的的思考

    这次只有成绩统计是全的…… 看上去好像还是挺跌宕起伏的啊…… 话说亲爱的教练员又一次食言了,说好的10次就换呢?! 名次表只有前10次的了…… 不得不说前10次还是挺好的,10次考试,7次前十,5次前 ...

  8. Java面试题汇总---升级版(附答案)

    前几天写了Java面试题汇总---基础版,总结了面试中常见的问题及答案,那我今天基于昨天的话题做一次升级,也就是说,求职者除了要学习了解哪些常见的基础面试题之外,还得准备些什么呢? 对有工作经验的求职 ...

  9. Spring Boot2(十四):单文件上传/下载,文件批量上传

    文件上传和下载在项目中经常用到,这里主要学习SpringBoot完成单个文件上传/下载,批量文件上传的场景应用.结合mysql数据库.jpa数据层操作.thymeleaf页面模板. 一.准备 添加ma ...

  10. SCADA开源项目lite版本

    一.引子 自从看了老坏猫(江湖人称猫总)的SharpSCADA项目后,让我感觉耳目一新同时也对自动化数据采集有了更深入的认识,我相信有不少做上位机的朋友和我一样对这个项目非常好奇.我们做上位机的应用场 ...