接口类型

  • 在Go语言中接口(interface)是一种类型,一种抽象的类型。
  • interface是一组method的集合,是duck-type programming的一种体现。接口做的事情就像是定义一个协议(规则)

举例:只要一台机器有洗衣服和甩干的功能,我就称它为洗衣机。不关心属性(数据),只关心行为(方法)

为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型

为什么要使用接口

下面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?

案例:

type Cat struct{}

func (c Cat) Say() string { return "喵喵喵" }

type Dog struct{}

func (d Dog) Say() string { return "汪汪汪" }

func main() {
c := Cat{}
fmt.Println("猫:", c.Say())
d := Dog{}
fmt.Println("狗:", d.Say())
}

Go语言中为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。

接口的定义

Go语言提倡面向接口编程。

每个接口由数个方法组成,接口的定义格式如下:

type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2

}
  • 接口名:使用type将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有字符串功能的接口叫Stringer等。接口名最好要能突出该接口的类型含义。
  • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

实现接口的条件

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表。

针对上面的案例我们实现一个Sayer接口

type Sayer interface {
say()
}

因为Sayer接口里只有一个Say方法,所以我们只需要给Dog和Cat分别实现Say方法就可以实现Sayer接口了。

//定义dog和cat两个结构体:
type dog struct {} type cat struct {} // dog实现了Sayer接口
func (d dog) say() {
fmt.Println("汪汪汪")
} // cat实现了Sayer接口
func (c cat) say() {
fmt.Println("喵喵喵")
} func main() {
// 定义一个Sayer的切片类型
var animal []Sayer // 接口是一个类型
c := cat{}
d := dog{}
animal = append(animal, c, d)
fmt.Println(animal) // [{} {}]
}

接口的实现就是这么简单,只要实现了接口中的所有方法,就实现了这个接口。

接口类型变量

接口类型变量能够存储所有实现了该接口的实例。 例如上面的示例中,Sayer类型的变量能够存储dog和cat类型的变量。

func main() {
var x Sayer // 声明一个Sayer类型的变量x
a := cat{} // 实例化一个cat
b := dog{} // 实例化一个dog
x = a // 可以把cat实例直接赋值给x
x.say() // 喵喵喵
x = b // 可以把dog实例直接赋值给x
x.say() // 汪汪汪
}
// 注意: 接口是一个抽象的类型

值接收者和指针接收者实现接口的区别

使用值接收者实现接口和使用指针接收者实现接口有什么区别呢?接下来我们通过一个例子看一下其中的区别。

我们有一个Mover接口和一个dog结构体。

type Mover interface {
move()
} type dog struct {}

值接收者实现接口

func (d dog) move() {
fmt.Println("狗会动")
}

此时实现接口的是dog类型:

func main() {
var x Mover
var wangcai = dog{} // 旺财是dog类型
x = wangcai // x可以接收dog类型
var fugui = &dog{} // 富贵是*dog类型
x = fugui // x可以接收*dog类型
x.move()
}

从上面的代码中我们可以发现,使用值接收者实现接口之后,不管是dog结构体还是结构体指针*dog类型的变量都可以赋值给该接口变量。因为Go语言中有对指针类型变量求值的语法糖,dog指针fugui内部会自动求值*fugui

指针接收者实现接口

同样的代码我们再来测试一下使用指针接收者有什么区别:

func (d *dog) move() {
fmt.Println("狗会动")
}
func main() {
var x Mover
var wangcai = dog{} // 旺财是dog类型
x = wangcai // x不可以接收dog类型
var fugui = &dog{} // 富贵是*dog类型
x = fugui // x可以接收*dog类型
}

此时实现Mover接口的是*dog类型,所以不能给x传入dog类型的wangcai,此时x只能存储*dog类型的值。

类型与接口的关系

  • 一个类型实现多个接口

一个类型可以同时实现多个接口,而接口间彼此独立,不知道对方的实现。 例如,狗可以叫,也可以动。我们就分别定义Sayer接口和Mover接口,如下:Mover接口。

// 定义两个接口
type Sayer interface {
say()
}
type Mover interface {
move()
} // dog结构体
type dog struct{
name string
} // dog实现say方法
func (d dog) say() {
fmt.Println("汪汪汪")
} // dog实现move方法
func (d dog) move() {
fmt.Println("跑...")
} func main() {
var d = dog{name:"旺财"} // dog实现了say,move方法
var s Sayer
var m Mover
s = d
m = d
s.say() // 汪汪汪
m.move() // 跑...
}
  • 多个类型实现同一接口

Go语言中不同的类型还可以实现同一接口 首先我们定义一个Mover接口,它要求必须有一个move方法。

// Mover 接口
type Mover interface {
move()
} // dog和car结构体
type dog struct {
name string
}
type car struct {
brand string
}
// dog类型实现Mover接口
func (d dog) move() {
fmt.Printf("%s会跑\n", d.name)
} // car类型实现Mover接口
func (c car) move() {
fmt.Printf("%s速度70迈\n", c.brand)
}
func main() {
var x Mover
var a = dog{name: "旺财"}
var b = car{brand: "保时捷"}
x = a
x.move() // 旺财会跑
x = b
x.move() // 保时捷速度70迈
}

这个时候我们在代码中就可以把狗和汽车当成一个会动的物体来处理了,不再需要关注它们具体是什么,只需要调用它们的move方法就可以了。

  • 类型嵌套其他类型实现多个接口

// WashingMachine 洗衣机接口
type WashingMachine interface {
wash()
dry()
}
// 甩干器
type dryer struct{}
// 甩干器实现WashingMachine接口的dry()方法
func (d dryer) dry() {
fmt.Println("甩一甩")
} // 海尔洗衣机
type haier struct {
dryer //嵌套甩干器
}
// 海尔洗衣机实现WashingMachine接口的wash()方法
func (h haier) wash() {
fmt.Println("洗刷刷")
} func main() {
h := haier{dryer{}}
h.wash() // 洗刷刷
h.dry() // 甩一甩
var w WashingMachine
w = h
w.wash() // 洗刷刷
w.dry() // 甩一甩
}

接口嵌套

接口与接口间可以通过嵌套创造出新的接口。

// Sayer 接口
type Sayer interface {
say()
} // Mover 接口
type Mover interface {
move()
} // 接口嵌套
type animal interface {
Sayer // say()
Mover // move()
} type cat struct {
name string
} func (c cat) say() {
fmt.Println("喵喵喵")
} func (c cat) move() {
fmt.Println("猫会动")
} func main() {
var x animal
x = cat{name: "咪咪"}
x.move() // 猫会动
x.say() // 喵喵喵
}

空接口

定义

空接口是指没有定义任何方法的接口。因此任何类型都实现了空接口

空接口类型的变量可以存储任意类型的变量。因为任何类型都实现了空接口。

// 定义一个空接口x
type inter interface {} func main() {
var x inter
s := "Hello 沙河"
x = s
fmt.Printf("type:%T value:%v\n", x, x)
i := 100
x = i
fmt.Printf("type:%T value:%v\n", x, x)
b := true
x = b
fmt.Printf("type:%T value:%v\n", x, x)
}

空接口的应用

  • 空接口作为函数的参数

使用空接口实现可以接收任意类型的函数参数。

// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
func main() {
s := "Hello 沙河"
i := 100
b := true
str := struct {
name string
}{name:"xionger"}
show(s) // type:string value:Hello 沙河
show(i) // type:int value:100
show(b) // type:bool value:true
show(str) // type:struct { name string } value:{xionger}
}
  • 空接口作为map的值

使用空接口实现可以保存任意值的字典。

// 空接口作为map值
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "熊二"
studentInfo["age"] = 18
studentInfo["married"] = false
fmt.Println(studentInfo) // map[age:18 married:false name:熊二]

类型断言

空接口可以存储任意类型的值,那我们如何获取其存储的具体数据呢?

想要判断空接口中的值这个时候就可以使用类型断言,其语法格式

x.(T)
  • x:表示类型为interface{}的变量
  • T:表示断言x可能是的类型。

该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

func Show(x interface{}){
// 因为这个函数可以接受任意类型的变量,这时候要要用到断言
// 类型断言
v, ok := x.(int)
if !ok {
fmt.Println("断言错误",v)
}else {
fmt.Println("断言正确",v)
}
} func main() {
Show(100) // 断言正确 100
Show("熊二") // 断言错误 0
}

上面的示例中如果要断言多次就需要写多个if判断,这个时候我们可以使用switch语句来实现:

func justifyType(x interface{}) {
switch v := x.(type) {
case string:
fmt.Printf("x is a string,value is %v\n", v)
case int:
fmt.Printf("x is a int is %v\n", v)
case bool:
fmt.Printf("x is a bool is %v\n", v)
default:
fmt.Println("unsupport type!")
}
}

Go语言 - 接口的更多相关文章

  1. Swift中对C语言接口缓存的使用以及数组、字符串转为指针类型的方法

    由于Swift编程语言属于上层编程语言,而Swift中由于为了低层的高性能计算接口,所以往往需要C语言中的指针类型,由此,在Swift编程语言刚诞生的时候就有了UnsafePointer与Unsafe ...

  2. C语言接口与实现实例

    一个模块有两部分组成:接口和实现.接口指明模块要做什么,它声明了使用该模块的代码可用的标识符.类型和例程,实现指明模块是如何完成其接口声明的目标的,一个给定的模块通常只有一个接口,但是可能会有许多种实 ...

  3. [转]SQLITE3 C语言接口 API 函数简介

    SQLITE3 C语言接口 API 函数简介 说明:本说明文档属作者从接触 SQLite 开始认识的 API 函数的使用方法, 由本人翻译, 不断更新. /* 2012-05-25 */ int sq ...

  4. 基于Oracle OCI的数据访问C语言接口ORADBI .

    基于Oracle OCI的数据访问C语言接口ORADBI cheungmine@gmail.com Mar. 22, 2008   ORADBI是我在Oracle OCI(Oracle 调用接口)基础 ...

  5. 18 A GIF decoder: an exercise in Go interfaces 一个GIF解码器:go语言接口训练

    A GIF decoder: an exercise in Go interfaces  一个GIF解码器:go语言接口训练 25 May 2011 Introduction At the Googl ...

  6. opencv的C语言接口和C++接口差别(入门篇)

    opencv是一个开源的图像处理库,最经典的1.0版本号提供的接口都是C语言接口. 后来的opencv2.x版本号保留了C语言接口,可是提供了C++接口,当中的C语言接口仅仅是为了向后兼容,而C++接 ...

  7. GO语言学习(十八)Go 语言接口

    Go 语言接口 Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口. 实例 /* 定义接口 */ type interface ...

  8. C语言学习书籍推荐《C语言接口与实现:创建可重用软件的技术》下载

    <C语言接口与实现:创建可重用软件的技术>概念清晰.实例详尽,是一本有关设计.实现和有效使用C语言库函数,掌握创建可重用C语言软件模块技术的参考指南.书中提供了大量实例,重在阐述如何用一种 ...

  9. 07. Go 语言接口

    Go 语言接口 接口本身是调用方和实现方均需要遵守的一种协议,大家按照统一的方法命名参数类型和数量来协调逻辑处理的过程. Go 语言中使用组合实现对象特性的描述.对象的内部使用结构体内嵌组合对象应该具 ...

  10. 浅入浅出 Go 语言接口的原理

    浅入浅出 Go 语言接口的原理 接口是 Go 语言的重要组成部分,它在 Go 语言中通过一组方法指定了一个对象的行为,接口 interface 的引入能够让我们在 Go 语言更好地组织并写出易于测试的 ...

随机推荐

  1. 使用Prometheus监控snmp

    获取snmp信息 首先获取需要监控的snmp的基本信息,假设基本信息如下: snmp服务IP: 1.1.1.1 snmp community: public snmp exportor部署地址: 2. ...

  2. day58——模板继承、组件、自定义标签和过滤器、inclusion_tag、静态文件配置、url别名和反向解析、url命名空间

    day58 模板相关 模板继承(母版继承) 1. 创建一个xx.html页面(作为母版,其他页面来继承它使用) 2. 在母版中定义block块(可以定义多个,整个页面任意位置) {% block co ...

  3. Java基础扫盲系列(二)—— Java中BigDecimal和浮点类型

    一直以来我几乎未使用过BigDecimal类型,只有在DB中涉及到金额字段时听说要用Decimal类型,但是今天再项目代码中看到使用BigDecimal表示贷款金额. 本篇文章不是介绍BigDecim ...

  4. - Java中boolean类型占用多少个字节 MD

    目录 目录 Java中boolean类型占用多少个字节 1个bit(1位) 1个Byte(1字节,8位) 4个Byte(4字节,32位) 分析 官方文档中的描述 Markdown版本笔记 我的GitH ...

  5. vuejs应用开发前后端分离

    我们知道,无论是web应用还是app应用都有一个前后端,前端主要负责界面交互,后端负责数据持久化.在正规公司中一般是由两个团队来分别完成前端和后端的开发,在小公司或者个人开发的项目中,前后端很有可能是 ...

  6. tqdm()与set_description()的用法

    pbar=tqdm(range(55156))for i in pbar: # print(i) a=464443161*845113131 pbar.set_description("tr ...

  7. Python中的单例设计

    01. 单例设计模式 设计模式 设计模式 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案 使用 设计模式 是为了可重用代码.让代码更容易被他人理解.保 ...

  8. Celery在Django中的使用介绍

    Celery在Django中的使用介绍 Celery简介 celery是一个简单.灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必须工具. 它是一个专注于实时处理的任务队列,同时也 ...

  9. 使用MQ消息队列的优缺点

    前言 公司的项目一直都是在使用MQ的,但是由于使用的功能很简单,所以一直都是知其然不知其所以然,作为一个程序猿有必要了解每一个使用的技术,为什么使用它?它的优点是什么?缺点是什么?等等... 使用mq ...

  10. JSP+SpringMVC框架使用WebUploader插件实现注册时候头像图片的异步上传功能

    一.去官网下载webuploader文件上传插件 https://fex.baidu.com/webuploader/ 下载好后把它放到Javaweb项目的文件夹中(我放到了webcontent下面的 ...