重学Go语言 | Go Struct详解

1,077 阅读5分钟

我正在参加「掘金·启航计划」

在其他编程语言中(如Java),我们可以使用类(Class)来描述比较复杂的实体,比如定义用户信息(名称,年龄等)或者一个订单信息(订单号,下单时间等):

public class User{
    public String name;
    public int age;
    public String email;
}

Go语言并没有类(Class)的概念,如果想在Go语言表示用户、订单等复杂的实体,结构体(struct)是一个不错的选择。

什么是结构体

结构体,英文叫struct,是一种由一个或者多个成员组成的复合数据类型,通过结构体,我们可以在程序中定义复杂的数据实体,比如用户或订单信息等:

type Order struct{
    ID string
    CreatedAt int
}

结构体的使用

在使用Go内置的数据类型(如intfloatslice等)时,我们一般使用该数据类型直接定义变量,比如:

var i int 
var s string

之所以可以这样用,是因为这些基础数据类型是Go内置的数据类型。

但要使用结构体,则必须自己先创建一个结构体,也就是说,我们要自己创建一个结构体数据类型,这个结构体的成员的名称与类型由我们自己定义,然后再使用该结构体声明变量。

创建结构体

Go语言的结构体创建语法格式如下:

type StructName struct{
    FieldName FieldType
    FieldName FieldType
    ...
    FieldName FieldType
}

上面语法各个部分表示:

  • StructName表示结构体的名称,比如User,首字母大写表示可以在其他包中使用该结构创建变量。
  • FieldName表示成员名,如Name,首字母大写表示,可以在其他包中调用该成员。
  • FieldType表示成员的数据类型,比如int

上面我们已经讲解了创建结构体的语法格式,接下来,我们来创建一个结构体来表示用户信息:

type User struct{
    ID    string
    Name  string
    Age   int
    Email string
}

相同数据类型的成员也可以写在同一行:

type User struct{
    ID,Name,Email string
    Age           int
}

使用结构体声明变量

创建好结构体之后,就可以使用该结构体来声明变量了:

var u User

直接使用结构体定义变量,如果没有给各个成员赋初始化,则各成员为其数据类型的默认值。

通过结构体的字面量,可以在声明时给结构体的各个成员赋初始值:

var u1 User = User{
  ID:"001",
  Name:"test1",
  Age:20,
  Email:"test1@163.com",
}
​
u2 := User{
  ID:"002",
  Name:"test2",
  Age:23,
  Email:"test2@163.com",
}

也可以按结构体成员的顺序给结构体赋初值:

//正确
var u1 User = User{"001","test",20,"test@163.com"}
//不按成员顺序,错误
var u2 User = User{"001","test","test@163.com",20} 

访问结构体的成员

声明了结构体变量后,可以使用点号(.)来访问结构体的成员或给成员赋值:

u1.Name = "test11"
fmt.Println(u1.Name)

也可以对结构体的成员进行取址操作:

name := &u1.Name
&name = "ttt"
//u1的Name成员已经被修改
fmt.Println(u1.Name)

将结构体作为函数的参数

结构体不是引用类型,因此当将结构体变量作为实参传给函数时,会复制给形参,因此如果在函数内修改结构体,并不会生效

u := User{"001","test",20,"test@163.com"}
​
func ChangeName(u User){
    u.Name = "1111"
}
​
fmt.Println(u)

如果想要在函数内修改结构体,可以传入结构体指针:

func ChangeName(u *User){
    u.Name = "2222"
}
​
fmt.Println(&u)

结构体的比较

同一个结构体声明的变量可以进行比较,如果每个成员的值相等,则两个结构体变量相等,否则为不相等:

u1 := User{"001","test",20,"test@163.com"}
u2 := User{"001","test",20,"test@163.com"}
u3 := User{"002","test",20,"test@163.com"}
if u1 == u2 {
  fmt.Println("相等")
}else{
  fmt.Println("不相等")
}
​
if u1 == u3 {
    fmt.Println("相等")
}else{
  fmt.Println("不相等")
}

结构体的嵌套

结构体的嵌套有两种方式,一种是像其他数据类型一样,在一个结构体中,使用另一个结构体定义成员:

type Address struct {
    ID       int
    City     string
    Province string
}
​
type Customer struct {
    ID   int
    Name string
    Addr Address //另一个结构体
}
​
c := Customer{ID: 1, Name: "test", Addr: Address{1, "广东", "广州"}}
​
fmt.Println(c.Addr.ID)
fmt.Println(c.Addr.City)

如上代码所示,Customer结构体要访问Address结构体的成员,必须通过成员名,访问路径变长。

另外一个是使用匿名嵌入的方式:

type Customer struct {
    ID   int
    Name string
    Address
}
​
c := Customer{ID: 1, Name: "test", Address: Address{1, "广东", "广州"}}
fmt.Println(c.City)

匿名嵌入的方式,可以让最外层的结构体像访问自己的成员一样,访问嵌入结构体的成员,不过如果有同名成员,比如上面两个结构体都有成员ID,这时候要获取匿名结构体的ID,仍然只能通过嵌入结构体来访问:

fmt.Println(c.Address.ID)

结构体不能包含自己,但可以包含一个指向自己类型的指针:

//错误
type Node struct{
    ID
    Name 
    Node
}
​
//错误
type Node struct{
    ID
    Name 
    n Node
}
​
//正确
type Node struct{
    ID
    Name 
    *Node
}
​
//正确
type Node struct{
    ID
    Name 
    n *Node
}

小结

结构体是一种很灵活的结构体,通过其中的成员,可以描述不同的实体,所以在开发中会经常使用。

在这篇文章中,我们主要讲了以下几点:

  • 什么是结构体
  • 如何创建结构体、使用结构体声明变量
  • 结构体的比较
  • 内嵌结构体