Skip to main content

类型系统

go语言是强类型语言,理解类型系统对于深入学习go至关重要.

  • 基础类型和复合类型
  • 接口类型和实例类型
  • 值语义和引用语义
  • 名义类型和结构类型

名义类型和结构类型是类型系统中两个重要概念.

在初次学习类型系统是遇到了好多新概念,诸如名义类型,结构类型,值语义和引用语义等.

维度2:值语义和引用语义

值语义:赋值或传时,创建数据的完整副本,修改副本不影响原始数据.

引用语义:赋值或传递时,创建数据的引用,通过引用修改数据会影响原始数据

值语义和引用语义的主要区别在于对于副本的修改是否会影响原始数据.

值语义:基本类型,数组,结构体

引用语义类型:切片,map,通道和函数值

值语义,适合小对象,并发安全

引用语义,适合大对象或需要共享状态的数据

维度3:值语义和引用语义

值语义和引用语义的差别在于赋值,比如下面例子:

go
1
2
3
b=a
b.modify()

如果b的修改不会影响到a的值,那么该类型属于值类型,否则该类型属于引用类型.

go语言中类型的值语义表现的非常彻底,是因为数组.

go语言中的数组和基本类型没有区别,是很纯粹的值类型.

go
1

类型的对象实例与接口之间的关系?

go
1
2
3
4
5
type MyFunc func(int) int
func example1(x int) int { return x * 2 }
var f1 MyFunc = example1 // 错误!类型不匹配

初次看到这个问题,是有些疑惑的,为什么不匹配呢?

其实是自己对于类型系统的错误理解.

既然了解不匹配的原因,哪如何修改呢?可通过强制类型转化

go
1
2
3
4
5
6
7
8
9
10
type MyFunc func(int) int
func fn(x int) int { return x * 2 }
func main() {
var f1 MyFunc = MyFunc(fn)
fmt.Println("result", f1(2))
}

数组和切片就像是孪生兄弟似的,很像,但又有很明显的区别.

数组是容量在声明时必须指定,属于值类型

切片是基于数组的可变容量,属于引用类型

维度4:名义类型和结构类型

go语言是名义类型系统,所谓名义类型系统是指基于名称来判断类型是否相同,即使两个类型的结构完全一样,如果名字不同,其就是不同的类型.

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type one int
type two int
func main() {
var a any
a = one(10)
//类型断言
if tmp, ok := a.(two); ok {
fmt.Println("one", tmp)
} else {
fmt.Println("no")
}
}

虽然one和two名称不同,是两个不同的类型,但其底层类型相同,因此可以强制类型转换.

结构类型系统是基于类型的结构或形状来判断类型是否相同,如果两个类型的结构相同,那么其就是兼容的.

虽然go语言是名义类型系统,但接口的存在使其也具备一定的结构类型系统特性,因此使用起来更加灵活.

接口

接口赋值

接口赋值有什么用,

接口赋值在go语言中分为以下两种情况:

  • 将对象实例赋值给接口
  • 将一个接口赋值给另一个接口
go
1

将一个接口赋值给另一个接口

在go语言中,只要两个接口拥有相同的方法列表,那么其就是等同的,是可以相互赋值的.

接口赋值并不要求两个接口完全等价,如果接口A的方法列表是接口B方法列表的子集,那么接口B就可以赋值给接口A,

接口查询