Go通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型。一个带属性的结构体试图表示一个现实世界中的实体。结构体是复合类型(composite types),当需要定义一个类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。然后可以访问这些数据,就好像它是一个独立实体的一部分。结构体也是值类型,因此可以通过new函数来创建。
组成结构体类型的那些数据称为字段(fields)。每个字段都有一个类型和一个名字;在一个结构体中,字段名字必须是唯一的。
我们为什么需要结构体?
编程语言,终究是为了解决我们现实生活中的问题,生活中的事物(如人)都有属性(嘴、胳膊)和方法(行走、跳跃)。使用之前的普通类型和数组来表示这些是不方便的,结构体就像其它编程语言中的类(class),包含了一系列的属性和方法,结构体能够更好的描述事物,更好的解决问题。
tips:属性即字段
结构体定义
结构体定义的一般方式如下:
type identifier struct {
field1 type1
field2 type2
...
}
示例:
type Student struct {
Name string
Age int
Score float64
}
type T struct{a,b int}
也是合法的语法,它更适用于简单的结构体。
结构体的字段可以是任何类型,甚至是结构体本身,也可以是函数或者接口。可以声明结构体类型的一个变量,然后像下面这样给它的字段赋值:
var s T
s.a = 6
s.b = 8
数组可以看作是一种结构体类型,不过它使用下标而不是具名的字段。
创建结构体实例
创建结构体实例有两种方式,一种是普通方式(var t T
)创建,另一种是使用new()方法来创建对应结构体的实例。
普通方式创建结构体实例
声明var t T
会给t分配内存,并零值化内存,这个时候t的类型是T。语法如下
var t T
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p Person
p = Person{Name:"黄忠", age:35}
fmt.Println(p)
}
new()创建结构体实例
使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针: var t *T = new(T)
,如果需要可以把这条语句放在不同的行(比如定义是包范围的,但是分配却没有必要在开始就做)。
var t *T = new(T)
写这条语句的惯用方法是:t := new(T)
,变量 t 是一个指向 T 的指针(t的类型是*T),此时结构体字段的值是它们所属类型的零值。
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p *Person = new(Person)
(*p).Name = "黄忠" //也可以简写成 p.Name = "黄忠",编译器在编译时会优化自动帮我们加上
(*p).age = 34 //也可以简写成 p.age = 34
fmt.Println(*p)
}
在创建一个结构体变量后,如果没有给字段赋值,都对应一个零值(默认值)。在首次使用结构体里的引用字段的时候一定要先make()或者使用字面量的方式初始化才能再使用。
例如:
package main
import "fmt"
type Person struct {
Name string
Age int
Scores [5]float64
ptr *int //指针
slice []int //切片
mp map[string]string //map
}
func main() {
var p Person
p.Name = "黄忠"
p.Age = 34
p.Scores[0] = 12
//使用引用类型字段,要先初始化内存,才能使用,或者使用字面量方式初始化
p.ptr = new(int)
*p.ptr = 4
p.slice = []int{1, 2, 3, 4}
p.mp = make(map[string]string)
p.mp["test"] = "hello"
fmt.Println(p)
}
结构体实例初始化
初始化实例的常规方式如下:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
var p3 Person
p3.Name = "狄仁杰"
p3.age = 34
fmt.Println(p3)
}
上面的代码中使用了(.语法
)来给属性/字段赋值,在Go语言中这叫选择器(selector),无论变量是一个结构体类型还是一个结构体类型指针,都使用同样的 选择器符(selector-notation) 来引用结构体的字段/属性。(注:字段即属性)
还有更简单的方式就是使用混合字面量语法(composite literal syntax)
时间间隔(开始和结束时间以秒为单位)是使用结构体的一个典型例子:
type Interval struct {
start int
end int
}
intr := Interval{0, 3} //(A)
intr := Interval{end:5, start:1} //(B)
intr := Interval{end:5} //(C)
在(A)中,值必须以字段在结构体定义时的顺序给出,& 不是必须的。(B)显示了另一种方式,字段名加一个冒号放在值的前面,这种情况下值的顺序不必一致,并且某些字段还可以被忽略掉,就像(C)中那样。
示例:
package main
import "fmt"
type Person struct {
Name string
age int
}
func main() {
//方法一:混合字面量方法,
var p Person
p = Person{Name:"黄忠", age:35}
//方法2:混合字面量方法(和上面的不同点是没有指定属性名,这里值的顺序必须按照属性顺序来写)
var p2 Person
p2 = Person{"小乔", 16}
fmt.Println(p)
fmt.Println(p2)
}
聊聊给结构体字段赋值
来看看下面的代码:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
var p *Person = new(P