二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Shell编程

  • Go编程笔记

  • 前端编程笔记

  • Go学习笔记

    • 基础部分

      • 基础知识
      • 数据类型
      • fmt的格式化输入输出
      • 运算符
      • 流程控制
      • 认识函数
      • 包的操作
      • 项目工程-在同级目录
      • 指针(pointer)
      • 数组(array)
      • 切片(slice)
      • 字典(map)
      • 结构体(struct)
      • 匿名组合
      • 方法
      • 接口
        • 1,概述
        • 2,接口的使用。
          • 1,接口定义。
          • 2,接口实现。
        • 3,示例学习。
          • 1,接口的定义与实现。
          • 2,多态的呈现。
          • 3,接口的继承。
          • 4,接口的转换。
          • 5,空接口。
          • 6,接口类型查询。
          • 1,comma-ok 断言
      • error接口
      • panic
      • recover
      • 字符串操作
      • 字符串转换
      • 正则表达式
      • JSON处理
      • 文件的操作
      • goroutine
      • channel
      • select
      • sync-lock锁
      • 网络概述
      • Socket编程
      • 发文件与聊天室
      • http编程
      • 爬虫实战
      • 单元测试了解
    • web框架

    • orm框架

  • Vue-21年学习笔记

  • Vue-22年重学笔记

  • 编程世界
  • Go学习笔记
  • 基础部分
二丫讲梵
2021-07-10
目录

接口

文章发布较早,内容可能过时,阅读注意甄别。

# 1,概述

在 Go 语言中,接口是一个自定义类型,接口类型具体描述了一系列方法的集合。

接口类型是一种抽象的类型,它不会暴漏出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合,它们只会展示出它们自己的方法。因此接口类型不能将其实例化。

Go 通过借口实现了鸭子类型(duck-typing):“当看到一只鸟走起来像鸭子,游泳起来像鸭子,叫起来也像鸭子,那么这只鸟就可以被称为鸭子”。我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。

  • 同一个结构体可以实现多个接口
  • 接口可以嵌套
  • 关于接口需要注意的是,只有当有两个或两个以上的具体类型必须以相同方式进行处理的才需要定义接口。不要为了接口而定义接口,那样只会增加不必要的抽象,导致不必要的运行时损耗。

# 2,接口的使用。

# 1,接口定义。

type Humaner interface {
	SayHi()
}
1
2
3
  • 接口命名习惯以er结尾
  • 接口只有方法声明,没有实现,没有数据字段
  • 接口可以匿名嵌入其他接口,或嵌入到结构中

# 2,接口实现。

接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是由用户定义的类型实现,一个实现了这些方法的具体类型是这个接口类型的实例。

如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的值存入接口类型的值。

# 3,示例学习。

# 1,接口的定义与实现。

package main

import "fmt"

type Humaner interface {
	sayhi()
}

type Student struct {
	name string
	id   int
}

//Student 通过上边定义的接口实现了其方法
func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
	addr  string
	group string
}

//Teacher 通过上边定义的接口实现了其方法
func (tmp *Teacher) sayhi() {
	fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.addr)
}

//Mystr 通过上边定义的接口实现了其方法
type Mystr string

func (tmp *Mystr) sayhi() {
	fmt.Printf("Mystr[%s] sayhi\n", *tmp)
}

func main() {
	//定义接口类型的变量
	var i Humaner

	//只要实现了此接口方法的类型,那么这个类型的变量(接收者类型)就可以给i赋值
	s := &Student{"erya", 666}
	i = s
	i.sayhi()

	t := &Teacher{"bj", "go"}
	i = t
	i.sayhi()

	var str Mystr = "hello go"
	i = &str
	i.sayhi()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 2,多态的呈现。

其一:

package main

import "fmt"

type Humaner interface {
	sayhi()
}

type Student struct {
	name string
	id   int
}

//Student 通过上边定义的接口实现了其方法
func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
	addr  string
	group string
}

//Teacher 通过上边定义的接口实现了其方法
func (tmp *Teacher) sayhi() {
    fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group)
}

//Mystr 通过上边定义的接口实现了其方法
type Mystr string

func (tmp *Mystr) sayhi() {
	fmt.Printf("Mystr[%s, %d] sayhi\n", *tmp)
}

func main() {
	//定义接口类型的变量
	var i Humaner

	//只要实现了此接口方法的类型,那么这个类型的变量(接收者类型)就可以给i赋值
	s := &Student{"erya", 666}
	i = s
	i.sayhi()

	t := &Teacher{"bj", "go"}
	i = t
	i.sayhi()

	var str Mystr = "hello go"
	i = &str
	i.sayhi()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

其二:

package main

import "fmt"

type Humaner interface {
	sayhi()
}

type Student struct {
	name string
	id   int
}

//Student 通过上边定义的接口实现了其方法
func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

type Teacher struct {
	addr  string
	group string
}

//Teacher 通过上边定义的接口实现了其方法
func (tmp *Teacher) sayhi() {
	fmt.Printf("Teacher[%s, %s] sayhi\n", tmp.addr, tmp.group)
}

//Mystr 通过上边定义的接口实现了其方法
type Mystr string

func (tmp *Mystr) sayhi() {
	fmt.Printf("Mystr[%s] sayhi\n", *tmp)
}

//定义一个普通函数,函数的参数为接口类型
//只有一个函数,可以有不同表现,即为多态

func WhoSayHi(i Humaner) {
	i.sayhi()
}

func main() {
	s := &Student{"erya", 666}
	t := &Teacher{"bj", "go"}
	var str Mystr = "hello go"

	//调用同一个函数,会有不同表现,多态
	// WhoSayHi(s)
	// WhoSayHi(t)
	// WhoSayHi(&str)

	//创建一个切片
	x := make([]Humaner, 3)
	x[0] = s
	x[1] = t
	x[2] = &str

	//第一个返回下标,第二个返回下标对应的值
	for _, i := range x {
		i.sayhi()
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

# 3,接口的继承。

package main

import "fmt"

type Humaner interface { //子集
	sayhi()
}

type Personer interface { //超集
	Humaner //匿名字段,继承了sayhi()
	sing(lrc string)
}

type Student struct {
	name string
	id   int
}

//Student 实现了sayhi()
func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
	fmt.Println("Student在唱着: ", lrc)
}

func main() {
	//定义一个接口类型的变量
	var i Personer
	s := &Student{"erya", 666}
	i = s

	i.sayhi()
	i.sing("奔跑")

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

# 4,接口的转换。

这个地方涉及到一个概念,就是超集可以转换为子集,而子集不能转换为超集,其实很容易理解,因为超集里边将会有子集当中未定义的东西。

package main

import "fmt"

type Humaner interface { //子集
	sayhi()
}

type Personer interface { //超集
	Humaner //匿名字段,继承了sayhi()
	sing(lrc string)
}

type Student struct {
	name string
	id   int
}

//Student 实现了sayhi()
func (tmp *Student) sayhi() {
	fmt.Printf("Student[%s, %d] sayhi\n", tmp.name, tmp.id)
}

func (tmp *Student) sing(lrc string) {
	fmt.Println("Student在唱着: ", lrc)
}

func main() {
	//超集可以转换为子集,而子集不能转换为超集
	//其实很容易理解,因为超集里边将会有子集当中未定义的东西

	var iPro Personer //超集
	iPro = &Student{"erya", 666}
	var i Humaner //子集

	iPro = i //err

	i = iPro //可以,超集可以转换为子集
	i.sayhi()

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 5,空接口。

空接口(interface{})不包含任何的方法,正因为如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。它有点类似于 C 语言的 void*类型。

var v1 interface{} = 1		// 将int类型赋值给interface{}
var v2 interface{} = "abc"	// 将string类型赋值给interface{}
var v3 interface{} = &v2	// 将*interface{}类型赋值给interface{}
var v4 interface{} = struct{ x int }{1}
var v5 interface{} = &struct{ x int }{1}
1
2
3
4
5

当函数可以(或者需要)接受任意的对象实例时,我们会将其声明为 interface{},最典型的例子就是标准库 fmt 中 print xxx 系列的函数,例如:

func Printf(fmt string, args ...interface{})
func Println(args ...interface{})
1
2

下边用一个实例来理解。

package main

import "fmt"

func main() {
	//空接口类型,相当于万能类型,可以保存任意类型的值
	var i interface{} = 1
	fmt.Println("i = ", i)

	i = "abc"
	fmt.Println("i = ", i)
}
1
2
3
4
5
6
7
8
9
10
11
12

# 6,接口类型查询。

我们知道 interface 的变量里面可以存储任意类型的数值(该类型实现了 interface)。那么我们怎么反向知道这个变量里面实际保存了的是哪个类型的对象呢?目前常用的有两种方法:

  • comma-ok 断言
  • switch 测试

# 1,comma-ok 断言

Go 语言里面有一个语法,可以直接判断是否是该类型的变量:value, ok = element.(T)这里的 value 就是变量的值,ok 是一个 bool 类型,element 是 interface 变量,T 是断言的类型。

如果 element 里面确实存储了 T 类型的数值,那么 ok 返回 true,反之则返回 false。

首先可以通过 if 判断来进行查询。

package main

import "fmt"

type Student struct {
	name string
	id   int
}

func main() {
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello go"
	i[2] = Student{"erya", 666}

	//类型查询,也叫类型断言
	//第一个返回下标,第二个返回下标对应的值,data分别是i[0],i[1],i[2]
	for index, data := range i {
		//第一个返回的是值,第二个返回判断结果的真假
		if value, ok := data.(int); ok == true {
			fmt.Printf("x[%d] 类型为int, 内容为 %d\n", index, value)
		} else if value, ok := data.(string); ok == true {
			fmt.Printf("x[%d] 类型为string, 内容为 %s\n", index, value)
		} else if value, ok := data.(Student); ok == true {
			fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id)
		}
	}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

或者使用 switch 进行判断。

package main

import "fmt"

type Student struct {
	name string
	id   int
}

func main() {
	i := make([]interface{}, 3)
	i[0] = 1
	i[1] = "hello go"
	i[2] = Student{"erya", 666}

	//类型查询,也叫类型断言
	for index, data := range i {
		switch value := data.(type) {
		case int:
			fmt.Printf("x[%d] 类型为int, 内容为 %d\n", index, value)
		case string:
			fmt.Printf("x[%d] 类型为string, 内容为 %s\n", index, value)
		case Student:
			fmt.Printf("x[%d] 类型为Student, 内容为name = %s, id = %d\n", index, value.name, value.id)
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
微信 支付宝
#go
上次更新: 2024/06/13, 22:13:45
方法
error接口

← 方法 error接口→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式