Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型。一个带属性的结构体试图表示一个现实世界中的实体。结构体是复合类型(composite types),当需要定义一个类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。然后可以访问这些数据,就好像它是一个独立实体的一部分。结构体也是值类型,因此可以通过 new 函数来创建。
定义结构体
1、结构体定义的一般方式如下:
type identifier struct {
field1 type1
field2 type2
...
}
type T struct {a, b int} 也是合法的语法,它更适用于简单的结构体。
结构体里的字段都有名字,像 field1、field2 等,如果字段在代码中从来也不会被用到,那么可以命名它为 _。
结构体的定义只是一种内存布局的描述,只有当结构体实例化时, 才会真正地分配内存。因此必须在定义结构体并实例化后才能使用结构体的字段。
实例化结构体
2、基本实例化形式
以var的方式声明结构体即可完成实例化,基本格式如下:
var ins T
// T 为结构体类型, ins为结构体实例。
用结构体表示的点结构( Point )的实例化过程请参见下面的代码:
type Point struct {
X int
Y int
}
var p Point // p是值
p.X = 10
p.Y = 20
在例子中,使用"."来访问结构体的成员变量,如p.X 和p.Y 等。结构体成员变量的赋值方法与普通变量一致。
3、创建指针类型的结构体
还可以使用new 关键字对类型(包括结构体、整型、浮点数、字符串等)进行实例化,结构体在实例化后会形成指针类型的结构体。
使用new 的格式如下:
ins := new(T)
// T 为类型,可以是结构体、整型、字符串等。
// ins: T 类型被实例化后保存到ins 变量中, ins的类型为*T,属于指针。
Go语言让我们可以像访问普通结构体一样使用"."访问结构体指针的成员。
下面的例子定义了一个玩家(Player)的结构,玩家拥有名字、生命值和魔法值,实例化玩家(Player)结构体后,可对成员进行赋值,代码如下:
type Player struct{
Name string
HealthPoint int
MagicPoint int
}
tank :=new(Player)
task.Name = "Canon"
tank.HealthPoint =300
经过new 实例化的结构体实例在成员赋值上与基本实例化的写法一致。
4、取结构体的地址实例化
在Go语言中,对结构体进行"&"取地址操作时,视为对该类型进行一次new的实例化操作。取地址格式如下:
ins := &T{}
// T表示结构体类型
// ins为结构体的实例,类型为*T ,是指针类型。
示例:使用结构体定义了一个命令行指令(Command),指令中包含名称、变量关联和注释等。
对Command进行指针地址的实例化,并完成赋值过程。
type Command struct{
Name string
Var *int
Comment string
}
var version int = 1
cmd := &Command{} // 取结构体地址进行实例化
cmd.Name = "version"
cmd.Var = &version
cmd.Comment = "show version"
简写为:
cmd := &Command{"version",1,"show version"}
使用工厂方法创建结构体实例
取地址实例化是最广泛的一种结构体实例化方式。可以使用函数封装上面的初始化过程,代码如下:
func newCommand(name string, varref *int, comment string) *Command{
return &Command{
Name: name,
Var: varref,
Comment comment,
}
}
// 调用,进行实例化
cmd := newCommand(
"version",
&version,
"show version",
)
在Go语言中常常像上面这样在工厂方法里面使用初始化来实现构造函数。
可以将new实例化方式改成工厂方法进行实例化和初始化。
初始化结构体
结构体在实例化时可以直接对成员变量进行初始化。
初始化有两种形式:字段"键值对"形式及多值的列表形式。
键值对形式的初始化适合选择性填充字段较多的结构体;多个值的列表形式适合填充字段较少的结构体。
5、使用"键值对"初始化结构体
键值对初始化的格式如下:
ins := 结构体类型名{
字段1: 字段1的值,
字段2: 字段2的值,
...
}
示例:
type People struct{
name string
child * People
}
relation := &People{
name: "爷爷",
child: &People{
name: "爸爸",
child: &People{
name: "我",
},
},
}
6、使用多个值的列表初始化结构体
可以在"键值对"初始化的基础上忽略"键",多个值用逗号分隔。
格式:
ins := 结构体类型名{
字段1的值,
字段2的值,
...
}
注意:
• 必须初始化结构体的所有字段。
• 每一个初始化的填充顺必须与字段在结构体中声明的顺序一致。
• 键值对和值列表的初始化形式不能混用。
示例:
type Address struct{
Province string
City string
ZipCode int
PhoneNumber string
}
addr := Address{
"四川",
"成都",
610000,
"0",
}
7、初始化匿名结构体
匿名结构体没有类型名称,无须通过type关键字定义就可以直接使用。
匿名结构体的初始化写法由结构体定义和键值对初始化两部分组成。结构体定义时没有结构体类型名,只有宇段和类型定义。键值对初始化部分由可选的多个键值对组成,如下格式所示:
ins := struct{
//匿名结构体字段定义
字段1 字段类型1
字段2 字段类型2
...
}{
//字段初始化值-可选的
初始化字段1: 字段1的值,
初始化字段2: 字段2的值,
...
}
构造函数--结构体和类型的一系列初始化操作的函数封装
Go语言的类型或结构体没有构造函数的功能。结构体的初始化过程可以使用函数封装实现。
8、多种方式创建和初始化结构体--模拟构造函数重载
如果使用结构体描述猫的特性,那么根据猫的颜色和名字可以有不同种类的猫。那么不同的颜色和名字就是结构体的字段,同时可以使用颜色和名字构造不同种类的猫的实例,代码如下:
type Cat struct{
Color string
Name string
}
func NewCatByName(name string) *Cat{ // 定义用名字构造猫结构的函数,返回Cat 指针。
return &Cat{ // 取地址实例化猫的结构体。
Name: name, // 初始化猫的名字字段,忽略颜色字段。
}
}
func NewCatByColor(color string) *Cat{ // 定义用颜色构造猫结构的函数, 返回Cat指针。
return &Cat{
Color: color,
}
}
在这个例子中,颜色和名字两个属性的类型都是字符串。由于Go语言中没有函数重载,为了避免函数名字冲突,使用NewCatByName()和NewCatByColor()两个不同的函数名表示不同的Cat构造过程。
9、带有父子关系的结构体的构造和初始化--模拟父级构造调用(内嵌结构体)
黑猫是一种猫,猫是黑猫的一种泛称。同时描述着两种概念时,就是派生,黑猫派生子猫的种类。使用结构体描述猫和黑猫的关系时,将猫(Cat)的结构体嵌入到黑猫(BlackCat)中,表示黑猫拥有猫的特性,
然后再使用两个不同的构造函数分别构造出黑猫和猫两个结构体实例,如下:
type Cat struct {
Color string
Name string
}
type BlackCat struct {
Cat //嵌入Cat结构体,类似于派生,BlackCat拥有Cat的所有成员,实例化后可以自由访问Cat的所有成员。结构体也是一种数据类型,所以它也可以作为一个匿名字段来使用,
}
// 构造基类
func NewCat(name string) *Cat { // 定义了Cat的构造过程,使用名字作为参数,填充Cat结构体。
return &Cat{
Name: name,
}
}
// 构造子类
func NewBlackCat(color string) *BlackCat { // 使用color作为参数,构造返回BlackCat指针。
cat := &BlackCat{} // 实例化BlackCat结构,此时Cat也同时被实例化。
cat.Color = color // 填充BlackCat中嵌入的Cat颜色属性。BlackCat没有任何成员,所有成员都来自于Cat。
return cat
}
一个结构体可以内嵌到另一个结构体。是一种简单的"继承"。
type A struct {
ax, ay int
}
type B struct {
A
bx, by float32
}
func main() {
b := B{A{1, 2}, 3.0, 4.0}
/*或者
b := new(B)
b.ax = 1
b.ay = 2
b.bx = 3.0
b.by = 4.0
*/
fmt.Println(b.ax, b.ay, b.bx, b.by)
fmt.Println(b.A)
}