结构体类型struct
这一章我们来重点讲解一下Go的重要数据类型——结构体类型struct。一个结构体就是一个命名的元素序列,每个元素又叫做字段,每个字段都有一个类型和名字。从面向对象的角度看,结构体类型中的字段代表了该类型的属性,而与这些字段相关的方法,则可以看作针对这些属性的操作。
结构体定义
Go语言的结构体定义和C是非常类似的。以type开始,后面跟着结构体的名字、struct{字段名称 类型}。下面是通常的例子:
type Person struct {
Name string
Age int
}
Go语言还支持只提供类型,而不写字段名的方式,也就是匿名字段,或称为嵌入字段。当匿名字段是一个struct的时候,那么这个struct所拥有的全部字段都被隐式地引入了当前定义的这个结构体类型。
type Person struct {
Name string
Age int
}
type Staff struct {
Person //匿名字段
Phone int
Address string
}
不仅仅是struct字段,所有内置类型和自定义类型都可以作为匿名字段,比如string。pro03_7_1.go
type Person struct {
Name string
Age int
}
type Staff struct {
Person //匿名字段
Phone string
Address string
string //string作为匿名字段,当然也可以进一步使用切片slice []string
}
func main() {
staff := Staff{Person{"Sunny", 41}, "13901234567", "Peking", "5 star staff"}
fmt.Println(staff.string)
fmt.Println(staff.Name)
}
需要注意的
1)Go语言中没有public, protected, private的关键字,所以,如果你想让一个结构体可以被别的包访问的话,你需要把这个结构体的第一个字母大写。这是一种约定。
2)staff访问Person属性age和name的时候,就像访问自己所拥有的字段一样。staff.Person.Name和staff.Name都是正确的。
不过上面的第2条注意事项有产生了新的问题,就是如果在结构体里面有重名会怎样?重名也分两种情况:pro03_7_2.go
type Person struct {
Name string
Age int
}
type Staff struct {
Person //匿名字段
Phone string
Address string
Name string //重名覆盖
}
func main() {
staff := Staff{Person{"Sunny", 41}, "13901234567", "Peking", "Knight"}
fmt.Println(staff.Name, staff.Person.Name)
}
显示结果:Knight Sunny
我们上面说了,staff访问Person属性age和name的时候,就像访问自己所拥有的字段一样,不过在Staff里面也有了Name字段,如果访问的时候都是使用staff.Name,这个就像重写了字段Name,仿佛java里面的继承覆盖。不过,切记,切记,在Go中只存在嵌入而不存在继承的概念。在Go中是最外层优先的,staff.Name指向的是Staff里面的Name。
这样的重名是允许的,但是另外一种情况一定要避免,就是二义性的重名:
type StructA struct {
Name string
}
type StructB struct {
Name string
Age int
}
type StructC struct {
StructA
StructB
}
当我们
var s StructC
调用s.Name是调用的s.StructA.Name还是s.StructB.Name?
编译的时候会显示:ambiguous selector s.Name
结构体struct的赋值
在这里我着重讲一下struct的赋值,这个是GO比较有意思的一个语法点。pro03_7_3.go
type Person struct {
Name string
Age int
}
func main() {
var sunny Person
sunny = Person{"sunny", 41} //最常用的赋值方式,没有写key,必须要按照定义顺序赋值,这种赋值方式不能少写参数
fmt.Println(sunny)
sunny = Person{Age: 42, Name: "Go"} //key value赋值,顺序都可以颠倒
fmt.Println(sunny)
sunny.Name = "Bill" //这个赋值方式让C程序员很习惯,不过这种赋值方式可以少写某个参数
sunny.Age = 50
fmt.Println(sunny)
sunny.Age, sunny.Name = 10, "Peter" //这个就是具有Go语言特色的赋值方式
fmt.Println(sunny)
var peter Person //这里故意不给Name赋值,当然你也可以不给Age赋值。不赋值,系统会给默认值
peter.Age = 50
fmt.Println(peter)
}
结构体struct我就先讲到这里,因为struct的重点就是方法,这个我在后面会讲到。结构体的定义加上方法,就很类似于java的Class了。我要在这里再次强调切记,切记,在Go中只存在嵌入而不存在继承的概念。
除此之外我还要强调一点, Go是不支持泛型的! 很多java程序员,看到struct以后就会想到如何泛型,这个是大忌。
还有我在这里提一件事情,如果你写了struct,其实默认你应该实现一个String方法,这个方法类似于java里面的toString,这个方法是为了可阅读性输出使用的。我会在方法里面仔细讲解。