1.结构体定义#

结构体是由一系列属性组成的复合数据类型,每个属性都具有名称,类型,结构体将属性组合在一起由程序进行处理

在go语言中使用type声明一种新的类型,语法格式如下:

type Typename Formatter

某些时候我们想表示某种类型具体的含义,就可以定义一个自己的类型。

尤为重要的是,当你使用type声明了类型之后,在定义的类型将不是同一个类型,比如:

type num int var num1 int

num和num1不是同一个类型。如果要进行运算,需要转换。

1.1定义类型#

示例一#

通常我们定义一个变量, 如:age,表示年龄的时候,一般使用var 直接定义,如下

var Age int

但是,这样并不能体现出age的含义,那就可以定义一个type Age int

type Age int

而后在定义变量就成了这样

var Age Age 

如下

package main
import (
    "fmt"
)
type Age int
func main(){
    var Age Age
    fmt.Println(Age)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run type.go
0

我们可以进行赋值或者计算

Age += 18

如下:

package main
import (
    "fmt"
)
type Age int
func main(){
    var Age Age
    Age += 18
    fmt.Println(Age)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run type.go
18

示例二#

定义个User,格式是string类型

type User map[string]string

如下:

package main
import (
    "fmt"
)
type User map[string]string
func main(){
    me := make(User)
    me["addr"] = "Beijing"
    fmt.Println(me)
    fmt.Printf("%T\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run user.go
map[addr:Beijing]
main.User

示例三#

定义一个匿名函数类型

type Callback func(...string)

如下:

package main
import (
    "fmt"
)
type Callback func(...string)
func main(){
    var list Callback = func(args ...string){
        for i,v := range args{
            fmt.Println(i,":",v)
        }
    }
    list("a","b","c")
}

运行

[root@linuxea.com /opt/Golang/work5]# go run callback.go
0 : a
1 : b
2 : c

假如现在要定义一个结构,就需要结构体

1.2定义结构体#

假设现在定义一个用户的结构体,要声明就需要使用struct模式进行定义,模式就可以认为是属性,比如,用户就需要有id属性,name,生日等。

结构体的零值就是结构体内的类型的零值

定义User的结构体

type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}

而后var定义变量,类型即可

var user User

如下:

使用%#v打印出类型

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var user User
    fmt.Println(user)
    fmt.Printf("%#v",user)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct.go
{0  0001-01-01 00:00:00 +0000 UTC   }
main.User{ID:0, Name:"", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"", Remark:""}

初始化结构体1#

我们可以对这些进行初始化赋值

这种方式的初始化,需要对每个值进行赋值,通过定义的顺序进行赋值。

    var user2 User{1,"mark",time.Now(),"北京市","10010","mark"}

代码块

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var user User
    fmt.Println(user)
    fmt.Printf("%#v",user)


    var user2 User = User{1,"mark",time.Now(),"北京市","10010","mark"}
    fmt.Println(user2)
    fmt.Printf("%#v",user2)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct.go
{0  0001-01-01 00:00:00 +0000 UTC   }
main.User{ID:0, Name:"", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"", Remark:""}{1 mark 2019-10-04 17:28:24.492833938 +0800 CST m=+0.000264968 北京市 10010 mark}
main.User{ID:1, Name:"mark", Birthdy:time.Time{wall:0xbf5de20e1d600c92, ext:264968, loc:(*time.Location)(0x56a0c0)}, Addr:"北京市", Tel:"10010", Remark:"mark"}

我们可以对单个属性进行调用

    fmt.Println(user4.Name)

初始化结构体2#

如果要显示使用的是零值就可以什么都不传递进行配置

var user3 User = User{}
fmt.Println(user3)
package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var user3 User = User{}
    fmt.Println(user3)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct.go
main.User{ID:1, Name:"mark", Birthdy:time.Time{wall:0xbf5de2823a31218c, ext:494037, loc:(*time.Location)(0x56a0c0)}, Addr:"北京市", Tel:"10010", Remark:"mark"}{0  0001-01-01 00:00:00 +0000 UTC   }

我们可以对单个属性进行调用

    fmt.Println(user4.Name)

初始化结构体3#

指定属性名的方式进行赋值,如下:

尤为注意的是,这里括号内的{}的每一行都需要,

    var user4 User = User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }

我们可以对单个属性进行调用

    fmt.Println(user4.Name)

如下:

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var user4 User = User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }

    fmt.Println(user4)
    fmt.Printf("%#v\n",user4)   
    fmt.Println(user4.Name)
}

运行

{1 mark 2019-10-04 17:55:22.37708593 +0800 CST m=+0.000061322 上海 10086 }
main.User{ID:1, Name:"mark", Birthdy:time.Time{wall:0xbf5de3a29679dfea, ext:61322, loc:(*time.Location)(0x56a0c0)}, Addr:"上海", Tel:"10086", Remark:""}
mark

1.3定义结构体指针#

我们可以使用指针给结构体传值,假设结构体如下:

type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}

指针只需要直接使用*即可

var pointer *User

也可以直接进行赋值,为此定义user4

    var user4 User = User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }

而后使用指针赋值给*User

var pointer *User = &user4

代码块如下

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var user4 User = User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }
    var pointer *User = &user4
    fmt.Println(pointer)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run pointer.go
&{1 mark 2019-10-04 21:51:13.684714251 +0800 CST m=+0.000136163 上海 10086 }

传递控制大致可以如下即可

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var pointer1 *User = &User{}
    fmt.Println(pointer1)
    fmt.Printf("%#v",pointer1)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run pointer1.go
&{0  0001-01-01 00:00:00 +0000 UTC   }
&main.User{ID:0, Name:"", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"", Remark:""}

也可以使用new,比如:

  • new出来的都是指针类型,一般用于结构体指针,而在切片和映射本身就是引用类型,一般不会使用new,而是make
package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    var pointer1 *User = new(User)
    fmt.Println(pointer1)
    fmt.Printf("%#v",pointer1)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run pointer2.go
&{0  0001-01-01 00:00:00 +0000 UTC   }
&main.User{ID:0, Name:"", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"", Remark:""}

2.结构体操作#

结构体的获取和修改,我们只需要使用赋值的变量名.属性名即可。而修改直接重新赋值即可。

2.1获取#

定义了结构体后,如何通过变量访问里面的属性呢?,如下

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    user4 := User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }
}

此刻,我们访问一个属性,直接可以使用变量名.属性名即可,如下:

比如只单单获取id和name

fmt.Println(user4.ID,user4.Name)

如下:

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    user4 := User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }
    fmt.Println(user4.ID,user4.Name)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct2.go 
1 mark

2.2修改#

比如,我们直接修改tel

user4.Tel = "1008600"

代码块

[root@linuxea.com /opt/Golang/work5]# cat struct2.go
package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    user4 := User{
        ID:1,
        Name:"mark",
        Birthdy:time.Now(),
        Addr:"上海",
        Tel:"10086",
    }
    fmt.Println(user4.ID,user4.Name,user4.Tel)
    user4.Tel = "1008600"
    fmt.Println(user4.ID,user4.Name,user4.Tel)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct2.go 
1 mark 10086
1 mark 1008600

2.3指针结构体的修改#

我们直接使用上面的赋值修改即可

    user := &User{
        ID:2,
        Name: "feeker",
    }

修改,这里用到*来进行解引用,如下

    (*user).Name = "UZI"
    (*user).Tel = "10010"

如下:

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    user := &User{
        ID:2,
        Name: "feeker",
    }
    (*user).Name = "UZI"
    (*user).Tel = "10010"
    fmt.Printf("%#v",user)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct3.go
&main.User{ID:2, Name:"UZI", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"10010", Remark:""}

2.4指针结构的获取#

指针结构体在访问属性的时候,可以直接使用变量名.属性名来进行访问。

    user.Addr = "beijing"
    fmt.Printf("%#v\n",user)

user.Addr = "beijing"事实上也进行了解应用操作

如下:

package main
import (
    "fmt"
    "time"
)
type User struct {
    ID int
    Name string
    Birthdy time.Time
    Addr string
    Tel string
    Remark string
}
func main(){
    user := &User{
        ID:2,
        Name: "feeker",
    }
    (*user).Name = "UZI"
    (*user).Tel = "10010"
    fmt.Printf("%#v\n",user)

    user.Addr = "beijing"
    fmt.Printf("%#v\n",user)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run struct3.go
&main.User{ID:2, Name:"UZI", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"", Tel:"10010", Remark:""}
&main.User{ID:2, Name:"UZI", Birthdy:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, Addr:"beijing", Tel:"10010", Remark:""}

3.匿名结构体#

匿名结构体,顾名思义,就是没有名称的,也是只需要使用一次的。或者是函数内的,比如配置,web开发渲染模板时候使用。

匿名结构体会在内部声明一个和当前类型相同名称的属性名。在访问的时候可以使用变量名.属性名访问

现在,我们定义一个me的匿名结构体

    var me struct {
        ID int
        Name string
    }

简短声明

me := struct { ID int Name string }

如下:

package main
import "fmt"
func main(){
    var me struct {
        ID int
        Name string
    }
    fmt.Printf("%T\n",me)
    fmt.Printf("%#v\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run nm.go
struct { ID int; Name string }
struct { ID int; Name string }{ID:0, Name:""}

3.1匿名结构体的访问和修改#

我们可以直接使用匿名结构体的变量名.属性名即可进行访问和修改。大致如下:

    me.ID = 1
    me.Name = "mark"
    fmt.Println(me.ID,me.Name)
    fmt.Printf("%#v\n",me)

如下:

package main
import "fmt"
func main(){
    var me struct {
        ID int
        Name string
    }
    fmt.Printf("%T\n",me)
    fmt.Printf("%#v\n",me)

    me.ID = 1
    me.Name = "mark"
    fmt.Println(me.ID,me.Name)
    fmt.Printf("%#v\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run nm.go
struct { ID int; Name string }
struct { ID int; Name string }{ID:0, Name:""}
1 mark
struct { ID int; Name string }{ID:1, Name:"mark"}

4.结构体的组合#

一个结构体内的属性是另外一个结构体类型的,我们来了解结构体的命名嵌入。

示例1#

我们在购物平台购物填写地址的时候,一般会有很多的选况,我们简单用代码设置下:.

创建一个Address,其中包含了地址的属性信息

type Address struct {
    Region string
    Street string
    No     string
}

而后,创建 一个User的struct,在Addr的属性上填写上面结构体的名称Address

type User struct {
    ID int
    Name string
    Addr Address
}

那么在声明传递的时候和单个结构体是不一样的。因为在结构体中addr的属性是另外一个结构体,那么我们可以先定义一个变量来接收address。如下:

addr := Address{Region: "上海市",Street:"徐汇区",No:"1501"}

而后在将addr放到User赋值的结构体变量中,如下:

user := User{
    ID: 1,
    Name: "mark",
    Addr: addr,
}

示例2#

以上面例子为例,我们可以直接在定义结构体变量的时候直接就Addr调用Address{}配置,如下:

    user := User{
        ID:1,
        Name:"mark",
        Addr:Address{
            Region: "上海市",
            Street:"徐汇区",
            No:"1501"
        }
    }

如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}

func main(){
    user := User{
        ID:1,
        Name:"mark",
        Addr:Address{
            Region: "上海市",
            Street:"徐汇区",
            No:"1501",
        },
    }
    fmt.Printf("%#v\n",user)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run zh1.go
main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"1501"}}

假如现在要访问或者修改Address的Region,只需要 纵深即可,如user.Addr.Region:

    fmt.Println(user.Addr.Region)
    user.Addr.Region = "北京"
    fmt.Println(user.Addr.Region)

运行

[root@linuxea.com /opt/Golang/work5]# go run zh1.go
main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"1501"}}
上海市
北京

5.结构体匿名嵌入#

假设现在建立一个员工的的对象,其中包含,id,name,addr,salary,如下:

type Employee struct{
    ID int
    Name string
    Addr Address
    Salary float64
}

但是已有的结构体如下,如下:

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}

那么这个时候,就可以在Employee结构体中组合User,命名为user,如下:

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    user User
    Salary float64
}

这样一来,Employee中就组合了两个结构体。但是在Employee的结构体中,userUser命名的,但是可以不需要写user的,如果不写就是匿名嵌入,如下:

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
}

如果使用了匿名结构体,如User,在初始化的时候就可以使用User属性进行初始化,如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
}
func main(){
    me := Employee{}
    fmt.Printf("%#v\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run anonymouns.go
main.Employee{User:main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:""}}, Salary:0}

匿名结构体会通过类名自己创建属性

假如此刻用字面量初始化一个属性就可以 这样来初始化,如下:

    me02 := Employee{
        User: User{
            ID:1,
            Name: "mark",
            Addr:Address{
                Region:"上海市",
                Street:"徐汇区",
                ID:10,
            },
        },
        Salary: 10023.3,
    }

亦或者如下

    me.Salary = 1.78
    me.Name = "mark"
    me.Addr.Region = "上海市"
    fmt.Printf("%#v\n",me)

我么使用第一种方式,如下

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
}
func main(){
    me := Employee{}
    fmt.Printf("%#v\n",me)

    me02 := Employee{
        User: User{
            ID:1,
            Name: "mark",
            Addr:Address{
                Region:"上海市",
                Street:"徐汇区",
                No:"10501",
            },
        },
        Salary: 10023.3,
    }
    fmt.Printf("%#v\n",me02)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run anonymouns1.go
main.Employee{User:main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:""}}, Salary:0}
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10501"}}, Salary:10023.3}

我们可以访问结构体中的某个,如下:

这里需要注意到是,使用User: User{},那么在访问数据的时候会可以加上User,也可以不要

fmt.Println(me02.User.ID)
fmt.Println(me02.User.Addr.Region)
fmt.Println(me02.Salary)

fmt.Println(me02.Name)
fmt.Println(me02.Addr.Street)
fmt.Println(me02.Salary)

运行

[root@linuxea.com /opt/Golang/work5]# go run anonymouns1.go
main.Employee{User:main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:""}}, Salary:0}
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10501"}}, Salary:10023.3}
mark
上海市
10023.3
mark
徐汇区
10023.3

5.1匿名结构体属性名重复的操作#

那么,现在。有这样一个问题。如下:

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
    Name string
}

以上中,在Employee中的name和User中的name,此刻如果进行访问使用的会是那个?

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
    Name string
}
func main(){
    me02 := Employee{
        User: User{
            ID:1,
            Name: "mark",
            Addr:Address{
                Region:"上海市",
                Street:"徐汇区",
                No:"10",
            },
        },
        Salary: 10023.3,
    }
    fmt.Printf("%#v\n",me02)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run ff.go
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10"}}, Salary:10023.3, Name:""}
me02.Name: 

me02.Name: 结果为空。

我们重新赋值,使用me02.Name = "sean"

    me02.Name = "sean"
    fmt.Println("me02.Name:",me02.Name)

运行

[root@linuxea.com /opt/Golang/work5]# go run ff.go
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10"}}, Salary:10023.3, Name:""}
me02.Name: 
me02.Name: sean

现在就可以访问到。

当组合中的结构体有相同属性的是,会先访问到当前结构体的属性名,如果没有就会到组合结构体或者匿名结构体中找。如果匿名结构体和当前结构体都有相同的属性名,在访问的时候就需要指定匿名结构体的具体属性名。示例如下:

为了具体示例,先给当前结构体赋值,并打印当前Employee中的Name

    me02.Name = "sean"
    fmt.Println("me02.Name:",me02.Name)

而后访问匿名结构体中的嵌入的属性名User下的Name

    fmt.Println("me02.Name:",me02.User.Name)

代码块:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
    Name string
}
func main(){
    me02 := Employee{
        User: User{
            ID:1,
            Name: "mark",
            Addr:Address{
                Region:"上海市",
                Street:"徐汇区",
                No:"10",
            },
        },
        Salary: 10023.3,
    }
    fmt.Printf("%#v\n",me02)

    fmt.Println("me02.Name:",me02.Name)
    me02.Name = "sean"
    fmt.Println("me02.Name:",me02.Name)
    fmt.Println("me02.Name:",me02.User.Name)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run ff.go
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10"}}, Salary:10023.3, Name:""}
me02.Name: 
me02.Name: sean
me02.Name: mark

一般而言,我们使用全路径写法,即是me02.User.Name写法。

示例#

type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
    Name string
}

现在就需要进行全路径才能够赋值和获取,代码块如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Salary float64
    Name string
}
func main(){
    me02 := Employee{
        User: User{
            ID:1,
            Name: "mark",
            Addr:Address{
                Region:"上海市",
                Street:"徐汇区",
                No:"10",
                Name: "mark3",
            },
        },
        Salary: 10023.3,
        Name: "mark2",
    }
    fmt.Printf("%#v\n",me02)


    me02.User.Name = "sean"
    fmt.Println("me02.Name:",me02.User.Name)
    me02.Addr.Name = "sean1"
    fmt.Println("me02.Name:",me02.Addr.Name)
    me02.Name = "sean2"
    fmt.Println("me02.Name:",me02.Name)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run ql.go
main.Employee{User:main.User{ID:1, Name:"mark", Addr:main.Address{Region:"上海市", Street:"徐汇区", No:"10", Name:"mark3"}}, Salary:10023.3, Name:"mark2"}
me02.Name: sean
me02.Name: sean1
me02.Name: sean2

这样似乎不太明显,在看另外一个示例

示例#

嵌入的两个匿名结构体和匿名结构体中的冲突使用方式,结构体如下:

type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Address
    Salary float64
    Name string
}

如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    User
    Address
    Salary float64
    Name string
}
func main(){
    var me Employee
    fmt.Printf("%T,%#v\n",me,me)

    me.User.Name = "mark"
    me.Address.Name = "mark2"
    me.Name = "mark3"
    me.User.Addr.Name = "mark4"
    fmt.Println(me.User.Name)
    fmt.Println(me.Address.Name)
    fmt.Println(me.Name)
    fmt.Println(me.User.Addr.Name)
    fmt.Printf("%#v\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run q2.go
main.Employee,main.Employee{User:main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:"", Name:""}}, Address:main.Address{Region:"", Street:"", No:"", Name:""}, Salary:0, Name:""}
mark
mark2
mark3
mark4
main.Employee{User:main.User{ID:0, Name:"mark", Addr:main.Address{Region:"", Street:"", No:"", Name:"mark4"}}, Address:main.Address{Region:"", Street:"", No:"", Name:"mark2"}, Salary:0, Name:"mark3"}

6.结构体指针#

6.1 结构体指针命名嵌入#

如下,在User中命名嵌入了Address,但是使用的是指针类型的嵌入

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr *Address
}

如果是指针类型,那么User的零值就是nil

func main(){
    var mm User
    fmt.Printf("%#v\n",mm)
}

初始化与其他方式一样,不通的是在Address上需要使用指针方式

    mm = User{
        ID:1,
        Name: "mark",
        Addr:&Address{
            Region: "上海市",
            Street:"淞虹路",
            No:"12362",
            Name:"MarkSugar",
        },
    }
    fmt.Printf("%#v\n",)

代码块如下:

[root@linuxea.com /opt/Golang/work5]# cat proint.go
package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
    Name string
}
type User struct {
    ID int
    Name string
    Addr *Address
}
func main(){
    var mm User
    fmt.Printf("%#v\n",mm)
    mm = User{
        ID:1,
        Name: "mark",
        Addr:&Address{
            Region: "上海市",
            Street:"淞虹路",
            No:"12362",
            Name:"MarkSugar",
        },
    }
    fmt.Printf("%#v\n",mm)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run proint.go 
main.User{ID:0, Name:"", Addr:(*main.Address)(nil)}
main.User{ID:1, Name:"mark", Addr:(*main.Address)(0xc000084040)}

6.2.结构体指针匿名嵌入#

当在结构体嵌入的时候,直接使用的是指针类型的,就没有属性名了

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    *User
    Salary float64
}

如下:

func main(){
    var me Employee
    fmt.Printf("%#v\n",me)
    me = Employee{
        Salary: 1.5,
        User: &User{
            ID:1,
            Name:"mark",
            Addr:Address{
                Region:"北京市",
                Street:"望京",
            },
        },
    }
    fmt.Printf("%#v\n",me)
}

如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
type Employee struct{
    *User
    Salary float64
}
func main(){
    var me Employee
    fmt.Printf("%#v\n",me)
    me = Employee{
        Salary: 1.5,
        User: &User{
            ID:1,
            Name:"mark",
            Addr:Address{
                Region:"北京市",
                Street:"望京",
            },
        },
    }
    fmt.Printf("%#v\n",me)
    fmt.Println(me.User.Name)
    fmt.Println(me.User.Addr)
    fmt.Println(me.User.Addr.Region)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run nmqr.go
main.Employee{User:(*main.User)(nil), Salary:0}
main.Employee{User:(*main.User)(0xc00008e000), Salary:1.5}
mark
{北京市 望京 }
北京市

值类型是不会变,引用类型

如下:

和此前值类型一样,me赋值 后,然后赋值到me2,而后修改me2,并没有影响到me值,同样change修改了u.Name,也对me2没有影响

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
func change(u User){
    u.Name = "abc"
}
func main(){
    me := User{}
    me2 := me
    me2.Name = "mark"

    fmt.Printf("%#v\n",me)
    fmt.Printf("%#v\n",me2)

    change(me2)
    fmt.Printf("%#v\n",me2)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run z.go
main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:""}}
main.User{ID:0, Name:"mark", Addr:main.Address{Region:"", Street:"", No:""}}
main.User{ID:0, Name:"mark", Addr:main.Address{Region:"", Street:"", No:""}}

如果此时传递的是地址进去,就会发生改变,如下:

func changepoint(u *User){
    u.Name = "sean"
}

如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr Address
}
func change(u User){
    u.Name = "abc"
}
func changepoint(u *User){
    u.Name = "sean"
}
func main(){
    me := User{}
    me2 := me
    me2.Name = "mark"

    fmt.Printf("%#v\n",me)
    fmt.Printf("%#v\n",me2)

    change(me2)
    fmt.Printf("%#v\n",me2)

    changepoint(&me2)
    fmt.Printf("%#v\n",me2)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run z.go
main.User{ID:0, Name:"", Addr:main.Address{Region:"", Street:"", No:""}}
main.User{ID:0, Name:"mark", Addr:main.Address{Region:"", Street:"", No:""}}
main.User{ID:0, Name:"mark", Addr:main.Address{Region:"", Street:"", No:""}}
main.User{ID:0, Name:"sean", Addr:main.Address{Region:"", Street:"", No:""}}

如果此时Address是指针类型,那么结果就不一样了

type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr *Address
}

我们首先初始化me,而后用me2重新修改me2.Addr.Region = "上海市".

func main(){
    me := User{
        ID:1,
        Name:"mark",
        Addr:&Address{
            Region:"北京",
            Street:"大同",
            No:"1",
        },
    }
    me2 := me
    me2.Name = "mark"
    me2.Addr.Region = "上海市"

    fmt.Printf("%#v\n",me.Addr)
    fmt.Printf("%#v\n",me2.Addr)
}

如下

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr *Address
}
func main(){
    me := User{
        ID:1,
        Name:"mark",
        Addr:&Address{
            Region:"北京",
            Street:"大同",
            No:"1",
        },
    }
    me2 := me
    me2.Name = "mark"
    me2.Addr.Region = "上海市"

    fmt.Printf("%#v\n",me.Addr)
    fmt.Printf("%#v\n",me2.Addr)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run zz.go
&main.Address{Region:"上海市", Street:"大同", No:"1"}
&main.Address{Region:"上海市", Street:"大同", No:"1"}

结果可见指针已经修改了

6.3New函数#

对于结构体,我们可以尝试使用new函数来创建,这样别人就可以通过调用函数的方式来返回一个对象。

func NewUser(id int,name string,region,street,no string)User{
    return User{
        ID:id,
        Name:name,
        Addr: &Address{region,street,no},
    }
}

在main中调用即可

func main(){
    NewUser(1,"mark","上海市","淞虹路","100")
}

如下:

package main
import "fmt"
type Address struct {
    Region string
    Street string
    No     string
}
type User struct {
    ID int
    Name string
    Addr *Address
}
func NewUser(id int,name string,region,street,no string)User{
    return User{
        ID:id,
        Name:name,
        Addr: &Address{region,street,no},
    }
}
func main(){
    me := NewUser(1,"mark","上海市","淞虹路","100")
    fmt.Printf("%#v\n",me)
}

运行

[root@linuxea.com /opt/Golang/work5]# go run z1.go
main.User{ID:1, Name:"mark", Addr:(*main.Address)(0xc000062150)}

7.结构体可见性#

结构体首先字母大写则包外可见,否则仅包内可访问。结构体属性名首字母大写包外可见,否则仅包内可访问。

总结:结构体和属性名首字母大写才能包外访问。否则就不能够在包外访问。