结构体
什么是结构体
Golang中结构体用来将若干个域整合在一起,是一自定义类型。当我们需要将多个数据字段整合成在一起整体使用时可以使用结构体。
例如,一个公司员工一般具有firstName,lastName以及age等属性。因此把这三个属性放到一个结构体employee内部来整体考虑比较好。
声明结构体
type Employee struct {
firstName string
lastName string
age int
}这里声明了一个结构体类型Employee,该结构体类型具有三个域分别是firstName、lastName以及age。每个域都有其对应的数据类型,如果两个域对应的数据类型相同,那么可以更加紧凑的声明在一行,比如这里的firstName和lastName都是string类型,因此可以声明如下:
type Employee struct {
firstName, lastName string
age int
}由于这里新创建了一个数据类型Employee,因此我们称这里声明的结构体Employee为具名结构体。注意这里的Employee是一种结构体数据类型。我们可以声明该类型的结构体变量:
var employee Employee当然,我们也可以基于匿名结构体来声明结构体变量:
var employee struct {
firstName, lastName string
age int
}这里我们使用匿名结构体来声明了结构体变量employee。
创建结构体变量
首先我们来创建一个具名结构体变量。
Go Playground在线运行 这里我们声明了一个具名结构体类型Employee,然后定义了该类型的结构体变量emp1,emp1定义时显示的指明了结构体各个域名,采用这种方式定义结构体变量无需保持各个域的顺序,比如这里我们就将域lastName放到了最后。
接着我们定了另外一个结构体变量emp2,注意定义时隐掉了各个域的名称,因此需要保持各个域的顺序。
该程序执行结果如下:
我们再来创建一个匿名结构体变量。
Go Playground在线运行 这里我们声明了一个匿名结构体变量emp3,这里我们并未声明一个新的结构体类型,而是通过如下匿名结构体类型创建了一个匿名结构体变量:
该程序执行结果如下:
结构体变量的零值
如果我们声明了一个结构体变量,但是并不进行初始化赋值,那么该结构体变量的每个域将初始化为对应类型的零值。
Go Playground在线运行 这里声明了结构体变量emp4,但是并未进行初始化赋值。因此其各个域初始化为对应类型的零值。firstName和lastName初始化为string类型的零值"",age和salary初始化为int类型的零值0。该程序输出为:
我们也可以在声明结构体变量时初始化部分域,这时忽略掉的其他域初始化为对应类型的零值。
Go Playground在线运行 这里我们声明结构体变量emp5时,仅初始化了域firstName和lastName,这时被忽略的域age和salary则初始化为int类型的零值0。该程序输出如下:
访问结构体变量的域
Golang中通过.运算符来访问结构体变量的某个域。
Go Playground在线运行 这里通过emp6.firstName来访问结构体emp6的域firstName。该程序输出如下:
我们也可以通过该方式来给结构体变量某个域赋值:
Go Playground在线运行 这里先声明了结构体变量emp7,但是并未进行人工初始化,因此各个域均为零值。接着我们设置了firstName和lastName的值。该程序输出如下:
指向结构体变量的指针
我们可以创建指向结构体变量的指针。
Go Playground在线运行 这里的emp8是一个指向结构体变量的指针,指向的结构体变量类型为Employee。通过(*emp8).firstName可以访问该指针指向的结构体变量的属性firstName。该程序输出为:
前面(*emp8).firstName中首先对指针emp8进行了解引用*emp8,Golang允许我们简化成emp8.firstName,这种方式更加简洁:
Go Playground在线运行 该程序执行结果跟上一段程序保持一致。
匿名属性
我们创建的结构体类型中某些属性可以不指明属性名,仅指明其数据类型,结构体的这种属性称为匿名属性。下面我们创建一个结构体类型Person,其中含有两个匿名属性string和int。
我们通过一段程序来看下匿名属性的用法。
Go Playground在线运行 这里结构体类型Person含有两个匿名属性,p := Person{"Naveen", 50}定义了一个该结构体类型的变量。该程序输出{Naveen 50}。
尽快结构体的匿名属性不需要提供属性名,Golang中会默认使用其数据类型作为属性名。例如前面的结构体类型Person的两个匿名属性的属性名分别是string和int。
Go Playground在线运行 这里我们通过匿名属性的数据类型作为属性名来访问对应的属性string和int。该程序输出如下:
结构体嵌套
Golang中,结构体中的某个属性仍然可以是结构体,称这种结构体为嵌套结构体。
Go Playground在线运行 这里结构体类型Person中的属性address仍然是结构体类型。该程序输出为:
在嵌套结构体中,如果被嵌套的子结构体是个匿名属性,那么被嵌套的子结构体内部的属性会提升一层,这些被提升的属性看起来就像是父结构体自身属性一样。我们通过一个例子来理解下:
这里结构体类型Person含有一个属性Address是个匿名结构体属性。结构体Address的两个属性city和state可以通过父结构体Person直接访问,我们称Address的这两个属性称为提升属性。
Go Playground在线运行 这里我们直接通过父结构体p来访问子结构体Address的属性city和state,看起来就好像这两个属性是父结构体p本身的属性一样。该程序输出为:
结构体类型导出、结构体类型属性导出
如果一个结构体类型名称以大写字母开头,那么该结构体类型是一种导出类型,导出类型可以被其他包访问。类似的,如果结构体类型的属性名称以大写字母开头,这个属性也可以被其他包访问。下面我们通过一个例子来理解下。
在Golang工作目录的src文件夹下面创建一个子文件夹structs用来存放我们的项目。然后在structs文件夹下再继续创建一个子文件夹computer。 在computer目录下创建文件spec.go,并输入如下代码:
这里创建了一个包computer,导出了一个结构体类型Spec及其两个属性Maker和Price供其他包访问,另外一个属性model不是大写字母开头,因此未导出。下面我们在main包中引入包computer并使用。
在文件夹structs下面创建文件main.go,并输入如下代码:
此刻我们创建的文件目录应该如下:
前面的程序中我们引入了包computer,并且访问了其导出的结构体类型Spec及其导出的属性Maker和Price。我们可以通过如下命令来执行该程序:
该程序输出如下:
如果我们尝试访问未导出的属性model,Golang编译器会报错:
这里我们访问了未导出的属性字段model。执行会发现编译器报错:
判等
Golang中结构体是值类型(注意不是引用类型),如果两个结构体的所有属性类型兼容,那么这两个结构体就可以比较是否相等。当两个结构体的所有属性相等时这两个结构体也相等。
Go Playground在线运行 这里结构体变量name1和name2的属性均为string,string类型是可比较的,因此这两个结构体变量也是可比较的。输出如下:
如果结构体变量的属性不可比较,比如map,那么该结构体也是不可比较的。
Go Playground在线运行 这里结构体类型image的属性data是map类型,而Golang中map类型本身是不可比较的(这点可以回顾下前面介map的部分),因此会报错:
Last updated
Was this helpful?