baby sword‘s blog baby sword‘s blog
首页
  • java基础
  • java进阶
大数据
  • mysql

    • mysql索引
    • mysql日志
  • redis

    • 单机下的redis
    • 集群下的redis
  • Spring
  • springboot
  • RPC
  • netty
  • mybatis
  • maven
  • 消息队列
  • kafka
  • zookeeper
  • rocketmq
  • 七大设计原则
  • 创建型模式
  • 结构型模式
  • 行为型模式
  • SpringCloud

    • eureka
  • SpringCloud Alibaba

    • nacos
  • 计算机网络
  • 操作系统
  • 算法
  • 个人项目
  • 个人面试面经
  • 八股记忆
  • 工作积累
  • 逻辑题
  • 面试

    • 百度后端实习二面
GitHub (opens new window)

zhengjian

不敢承担失去的风险,是不可能抓住梦想的
首页
  • java基础
  • java进阶
大数据
  • mysql

    • mysql索引
    • mysql日志
  • redis

    • 单机下的redis
    • 集群下的redis
  • Spring
  • springboot
  • RPC
  • netty
  • mybatis
  • maven
  • 消息队列
  • kafka
  • zookeeper
  • rocketmq
  • 七大设计原则
  • 创建型模式
  • 结构型模式
  • 行为型模式
  • SpringCloud

    • eureka
  • SpringCloud Alibaba

    • nacos
  • 计算机网络
  • 操作系统
  • 算法
  • 个人项目
  • 个人面试面经
  • 八股记忆
  • 工作积累
  • 逻辑题
  • 面试

    • 百度后端实习二面
GitHub (opens new window)
  • 华仔聊技术

  • 业务设计

  • 场景设计

  • 运维

  • 安全

  • 面试

  • mac相关工具推荐

  • 开发工具

  • 人工智能

  • 推荐

  • 阅读

  • 工具

  • 计划

  • 产品

  • 云原生

  • go

    • 基础
    • go项目
    • go channel
    • go list
    • go testing模块
    • go指针
    • go实现LRU
    • go数据结构的内存实现
    • go interfaces
      • Go中的接口
        • 隐式实现
        • 接口类型
        • 类型断言
    • go项目分包相关
    • 协程实现原理
    • 垃圾回收与写屏障
    • gin
    • go中flag的使用
    • go dig
    • linux环境快速搭建go
  • QVM

  • 软件设计师

  • 极客时间

  • 单元测试

  • 其他
  • go
xugaoyi
2023-08-14
目录

go interfaces

Go语言中接口是一组方法的集合,它是Go语言的重要组成部分,面向接口编程使得代码之间的耦合度能够更低,并且更加方便测试。

# Go中的接口

Go中的接口分为两种,一种就是如上所说的方法的集合,另一种则是类型。

go
复制代码//
// @Description: 定义接口-人
//
type person interface {    // 第一种,所谓方法的集合
	eat()
        printName() string
}

var val interface{}       // 第二种,interface{}作为一种类型
1
2
3
4
5
6
7
8
9
10

# 隐式实现

Go中的接口和Java中的不同,Java中需要使用关键字implement来显式的声明一个类实现了某一个接口,而Go中则不需要。

与Java中Class对应,Go中则使用了struct结构体来表达类的概念,在Go中,任意一个struct实现了接口中的所有方法,那么则认为该struct实现了该接口。

go
复制代码type Amy struct {
	Name string
	Height float64
	Weight float64
	Age int
}

func (receiver Amy) eat()  {
	// do sth
}

func (receiver Amy) printName() string {
	return receiver.Name
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 接口类型

上面提到接口可以作为类型,更进一步的,接口在一定条件下可以与其他类型相互转换。

go
复制代码type Mike struct {
	Name string
}

type Jhon struct {
	Name string
}

func (receiver Mike) eat()  {
	// do sth
}

func (receiver Mike) printName() string {
	return receiver.Name
}

func (receiver Jhon) eat()  {
	// do sth
}

// Jhon这个struct没有实现printName这个方法

//
// @Description: 定义了一个做朋友的函数,接收的参数类型为person接口类型
// @param p1
// @param p2
//
func makeFriends(p1 person, p2 person) {
	fmt.Printf("%s and %s are friends now", p1.printName(), p2.printName())
}

func main() {
	makeFriends(Amy{Name: "Amy"}, Mike{Name: "Mike"})  // 正常运行打印
                                                           // Amy and Mike are friends now
	makeFriends(Amy{Name: "Amy"}, Jhon{Name: "Jhon"})  // 编译出错
                                                           // cannot use Jhon literal (type Jhon) as type person in argument to makeFriends:
                                                           //Jhon does not implement person (missing printName method)
}
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

上面的代码可以看出来,任意一个实现了person接口的struct都可以转换成person类型(Amy和Mike实现了接口,但是Jhon没有实现,于是报错)

而对于interface{}类型的变量则可以与任意类型进行转换,这里需要注意的是,仅仅是可以转换,而并非等于任意类型。Go中的interface{}和C语言中的void*有些类似,void*可以代表任意类型,但是interface{}只是具备着转换成为任意类型的能力,其本质上仍为interface{}类型。举个例子:

go
复制代码func main() {
	var w interface{}   // 标记①
	w = new(bytes.Buffer)   // 标记②
	w = new(int)        // 标记③
}
1
2
3
4
5
6

在Goland中用debug模式来查看变量w的类型

在标记①处,可以看到w的类型是interface{},并且值为nil

image-20230814210603418

在标记②处,可以看到w的类型是{interface{}|*bytes.Buffer},实质上仍为一个interface{}类型。注:关于interface{}底层源码可以参考此篇文章 (opens new window)

image-20230814210613660

在标记③处,可以看到w的类型是{interface{}|*int}

image-20230814210624742

# 类型断言

类型断言是一个使用在接口值上的操作。语法上它看起来像x.(T)被称为断言类型,这里x表示一个接口的类型和T表示一个类型。一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。 引用自Go语言圣经(中文版) (opens new window)

当interface{}类型作为函数参数时,可以传递任意类型的变量,参数在传递过程中会进行隐式的类型转换,转换成interface{}|T类型。由于传入参数类型的不可控性,因此常常会在函数内进行类型断言(就是常说的类型判断)。

go
复制代码func useInterface(i interface{})  {
	// 第一种方式,适合用于判断i是否为某一类型
	if convert, ok := i.(float64); ok {
		// do sth
	}

	// 第二种方式,使用switch来进行判断
	switch x := i.(type) {
	case float64:
		// do sth			
	case string:
		// do sth
	case int32:
		// do sth
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

如果直接使用x.(T)进行断言,如果x不是T类型,那么则会出现panic错误,这显然是不够优雅的,所以建议尽可能的使用convert, ok := x.(T)或者switch + x.(type)的方式来进行类型断言。

编辑 (opens new window)
上次更新: 2024/02/22, 14:03:19
go数据结构的内存实现
go项目分包相关

← go数据结构的内存实现 go项目分包相关→

最近更新
01
spark基础
02-22
02
mysql读写分离和分库分表
02-22
03
数据库迁移
02-22
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式