golang中的结构体格式如下:
type 结构体名称 struct {
字段名 字段类型
字段名 字段类型
}
下面定义一个结构体Point
,有坐标x, y两个整型字段:
type Point struct {
x int
y int
}
同种类型的字段可以写在一行,如下代码:
type Color struct {
r, g, b byte
}
结构体的定义只是一种内存布局的描述,只有当结构体实例化时,才会真正地分配内存。因此必须在定义结构体并实例化后才能使用结构体的字段。
如下代码所示:
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
var p Point // 或者 p := Point{}
p.x = 1
p.y = 0
// 打印:{1 0}
fmt.Println(p)
}
可以直接使用.
来访问结构体的成员变量。
除了使用上面的方式声明并初始化结构体外,还可以使用new
关键字,如下代码所示:
package main
import "fmt"
type Point struct {
x int
y int
}
func main() {
p := new(Point) // 也可以使用 p := &Point{}
p.x = 1
p.y = 0
fmt.Println(p)
}
与var p Point
这种方式不同的是,使用new
关键字创建的是Point
类型的指针变量,golang中同样可以使用.
来访问结构体指针的成员变量。
也可以在声明结构体变量时直接初始化:
func main() {
p := Point{
x: 1,
y: 0,
}
fmt.Println(p)
}
下面的代码中定义了两个结构体:猫和人,人养了一只宠物,代码如下:
package main
import "fmt"
// 结构体 猫
type Cat struct {
Name string
Weight float32
Color string
}
// 结构体 人
type Person struct {
Name string
Age int
// 养了一只猫
Pet *Cat
}
func main() {
// 初始化一只猫的指针
pet := &Cat{
Name: "kenny",
Weight: 20.3,
Color: "white",
}
// 初始化一个人
person := Person{
Name: "zhangsan",
Age: 25,
Pet: pet,
}
fmt.Println(person)
}
// 结构体作为函数参数
func test(msg *struct {
ID string
Name string
Age int
}) {
fmt.Printf("id: %v, name: %v, age: %v", msg.ID, msg.Name, msg.Age)
}
func main() {
// 调用函数,传入匿名的结构体
test(&struct {
ID string
Name string
Age int
}{
"asdfasdf",
"wangwu",
25,
})
// 打印结果:id: asdfasdf, name: wangwu, age: 25
}
golang没有Java中的类的概念,自然也没有类的构造方法,但是可以模拟结构体的构造函数,比如如下代码:
// 结构体: 猫
type Cat struct {
Name string
Color string
}
// 模拟猫的构造函数,传入姓名
func NewCatWithName(name string) *Cat {
return &Cat{
Name: name,
}
}
// 模拟猫的构造函数,传入猫的颜色
func NewCatWithColor(c string) *Cat {
return &Cat{
Color: c,
}
}
func main() {
// 调用“构造函数”创建两个猫
c1 := NewCatWithColor("black")
c2 := NewCatWithName("kenny")
fmt.Println(c1, c2)
}
golang中在一个结构体中嵌入另一个结构体,来完成类似于面向对象编程中的类的继承,如下代码:
// 结构体 表示动物
type Animal struct {
Color string
}
// 结构体 猫
type Cat struct {
Animal // 这里嵌入了Animal这个结构体,类似于Cat继承了Animal
Name string
}
func main() {
cat := Cat{}
// Cat类中没有声明Color,但是由于嵌入了Animal,所以也有了Color字段
cat.Color = "black"
cat.Name = "haha"
fmt.Println(cat)
}
在面向对象编程中,我们除了给类添加成员变量,还可以为类添加方法,在golang中定义结构体时,只能定义结构体中的成员变量,要为结构体添加方法,只能使用函数来实现,如下代码:
// 定义结构体 猫
type Cat struct {
Color string
}
// 为猫添加一个Eat()方法
func (c *Cat) Eat() {
fmt.Printf("The %s cat is eating...\n", c.Color)
}
func main() {
cat := &Cat{
Color: "black",
}
// The black cat is eating...
cat.Eat()
}
上面的代码为猫这个结构体添加了一个Eat
方法,在定义Eat
函数时,func
后面紧接着的是(c *Cat)
,这个(c *Cat)
叫做接收器,表示该函数作用的对象,因为是*Cat
,所以表示Eat
方法作用于Cat
指针上,于是在main
函数里,初始化了一个cat
指针,就可以直接使用cat.Eat()
来调用Eat
函数了。
golang里的接收器分为两种:指针类型的接收器和非指针类型的接收器。
上面的代码中展示的是指针类型的接收器,其实也可以定义非指针类型的接收器,如下代码:
// 点,包含两个坐标值
type Point struct {
X int
Y int
}
// 非指针类型的接收器
func (p Point) Add(a, b int) Point {
return Point{
X: p.X + a,
Y: p.Y + b,
}
}
func main() {
p := Point{
X: 0,
Y: 0,
}
p1 := p.Add(1, 1)
fmt.Println(p1)
}
在上面的代码中,Add
是Point
的一个方法,该方法接受两个整型数据,返回一个Point
,Add
函数的接收器是一个Point
结构体,而非结构体的指针类型。
指针类型接收器与非指针类型接收器的区别:
// 定义一个MyInt类型(就是int型)
type MyInt int
// 给MyInt型的变量添加一个isZero方法
func (i MyInt) isZero() bool {
return i == 0
}
func main() {
var i MyInt = 3
fmt.Println(i.isZero())
}
这里说的方法,指的是与某个结构体关联起来的函数,该函数具有接收器;而普通的使用func
定义的,称为函数。下面的代码展示了方法与函数的统一调用:
type Person struct {
}
// 定义Person的sing方法
func (p *Person) sing(song string) {
fmt.Printf("the person is singing %s\n", song)
}
// 定义一个单独的sing函数
func sing(song string) {
fmt.Printf("call function sing, the song is %s\n", song)
}
func main() {
// 定义f
var f func(string)
p := &Person{}
// 将f赋值为Person的成员方法
f = p.sing
f("My heart will go on")
// 将f赋值为独立的函数
f = sing
f("My heart will go on")
}
上面的代码证明了,如果方法与函数具有相同的签名,则可以使用与它们签名一致的变量保存该方法或函数。
golang中的结构体在声明时,可以只写字段类型而不写字段名称,这种结构体称为类型内嵌或匿名字段类型内嵌,如下代码所示:
// 定义Data结构体,该结构体字段只有类型没有名称
type Data struct {
string
int
float32
}
func main() {
// 初始化结构体
data := &Data{
string: "zhangsan",
int: 20,
float32: 80.5,
}
// 打印: &{zhangsan 20 80.5}
fmt.Println(data)
}
下面以一个例子说明结构体的内嵌,房子由门和窗户组成,如下代码:
// 窗户
type Window struct {
Width int
Height int
}
// 门
type Door struct {
Width int
Height int
}
// 房子
type House struct {
Window
Door
}
func main() {
// 初始化House的方式
house := &House{
Window: Window{
Width: 10,
Height: 10,
},
Door: Door{
Width: 20,
Height: 45,
},
}
fmt.Println(house)
}
上面的代码如果把Door这个结构体去掉,而直接把Door的结构写在House里,就成了嵌套的匿名结构体,如下代码:
// 窗户
type Window struct {
Width int
Height int
}
// 房子
type House struct {
Window
// 门直接以内嵌形式声明,而不单独声明结构体
Door struct {
Width int
Height int
}
}
func main() {
house := &House{
Window: Window{
Width: 10,
Height: 10,
},
// 初始化House时给Door赋值的方法如下:
Door: struct {
Width int
Height int
}{
Width: 20,
Height: 45,
},
}
fmt.Println(house)
}