3.7 结构体类型 struct

优质
小牛编辑
116浏览
2023-12-01

结构体类型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,这个方法是为了可阅读性输出使用的。我会在方法里面仔细讲解。