Go 通过结构体的形式支持用户自定义类型,或者叫定制类型。
Go 语言结构体是实现自定义类型的一种重要数据类型。
结构体是复合类型(composite types),它由一系列属性组成,每个属性都有自己的类型和值的,结构体通过属性把数据聚集在一起。
结构体类型和字段的命名遵循可见性规则。
方法(Method)可以访问这些数据,就好像它们是这个独立实体的一部分。
结构体是值类型,因此可以通过 new 函数来创建。
结构体是由一系列称为字段(fields)的命名元素组成,每个元素都有一个名称和一个类型。 字段名称可以显式指定(IdentifierList)或隐式指定(EmbeddedField),没有显式字段名称的字段称为匿名(内嵌)字段。在结构体中,非空字段名称必须是唯一的。结构体定义的一般方式如下:
type identifier struct {
field1 type1
field2 type2
...
}
结构体里的字段一般都有名字,像 field1、field2 等,如果字段在代码中从来也不会被用到,那么可以命名它为 _
。空结构体如下所示:
struct {}
具有6个字段的结构体:
struct {
x, y int
u float32
_ float32 // 填充
A *[]int
F func()
}
对于匿名字段,必须将匿名字段指定为类型名称T或指向非接口类型名称* T的指针,并且T本身可能不是指针类型。
struct {
T1 // 字段名 T1
*T2 // 字段名 T2
P.T3 // 字段名 T3
*P.T4 // f字段名T4
x, y int // 字段名 x 和 y
}
使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:
type S struct { a int; b float64 }
new(S)
new(S)为S类型的变量分配内存,并初始化(a = 0,b = 0.0),返回包含该位置地址的类型* S的值。
我们一般的惯用方法是:t := new(T)
,变量 t 是一个指向 T的指针,此时结构体字段的值是它们所属类型的零值。
也可以这样写:var t T
,也会给 t 分配内存,并零值化内存,但是这个时候 t 是类型T。
在这两种方式中,t 通常被称做类型 T 的一个实例(instance)或对象(object)。
使用点号符“.”可以获取结构体字段的值structname.fieldname。无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的表示法来引用结构体的字段。例如:
type myStruct struct { i int }
var v myStruct // v是结构体类型变量
var p *myStruct // p是指向一个结构体类型变量的指针
v.i
p.i
type Interval struct {
start int
end int
}
结构体变量有下面几种初始化方式,前面一种按照字段顺序,后面两种则对应字段名来初始化赋值:
intr := Interval{0, 3} (A)
intr := Interval{end:5, start:1} (B)
intr := Interval{end:5} (C)
复合字面量是构造结构体,数组,切片和字典的值,并每次都创建新值。声明和初始化一个结构体实例(一个结构体字面量:struct-literal)方式如下:
定义结构体类型Point3D和Line:
type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }
声明并初始化:
origin := Point3D{} // Point3D 是零值
line := Line{origin, Point3D{y: -4, z: 12.3}} // line.q.x 是零值
这里 Point3D{}
以及 Line{origin, Point3D{y: -4, z: 12.3}}
都是结构体字面量。
表达式 new(Type)
和 &Type{}
是等价的。&struct1{a, b, c}
是一种简写,底层仍然会调用 new ()
,这里值的顺序必须按照字段顺序来写。也可以通过在值的前面放上字段名来初始化字段的方式,这种方式就不必按照顺序来写了。
结构体类型和字段的命名遵循可见性规则,一个导出的结构体类型中有些字段是导出的,也即首字母大写字段会导出;另一些不可见,也即首字母小写为未导出,对外不可见。