Go关键字--struct

struct

golang语言中,struct有着至关重要的作用,struct用来定义一个抽象的数据结构。在程序设计中,首先对各种事物进行特征分析,提炼出各个事物最主要的特征信息,然后按照这些特征信息进行归类。每一个分类,有着自己的特征信息,且对于这个类而言,这些特征信息又不可或缺,正是有了这些零散的特征信息,才能更为形象的描述这个类。在程序设计中,基于类的设计,能够更好的保证数据完整性,也能更加准确的表达一个事物。golang中可以使用struct来定义包含诸多特征信息的抽象数据类型,也可以称之为类。定义结构体的语法格式是:

type name struct{
    field1 dataType
    field2 dataType
    ...
}

定义和使用结构体

定义结构体使用type和struct关键字组合完成。结构体中字段的作用域与字段的第一个字母有关,当结构体中字段第一个字母是大写时,能够在任何地方访问,如果结构体字段第一个字母不是大写,则只能在同一个包中被访问。下边这个示例,创建一个学生基本信息结构体,并对这个结构体进行初始化操作:

package main

import (
    "fmt"
)

// 学生基础信息
type Student struct {
    // 学号
    Id int
    // 学生姓名
    Name string
    // 学生年龄
    Age int
    // 手机号
    Mobile int
    // 专业
    Major string
}

func main() {
    // 创建Student指针类型变量
    s1 := new(Student)
    // 创建Student指针类型变量
    s2 := &Student{}
    // 创建Student类型变量
    s3 := Student{}

    // 创建Student指针类型变量, 并根据字段名附初始值
    s4 := &Student{
        Id:     20170901,
        Name:   "hzwy23",
        Age:    18,
        Mobile: 18107217021,
        Major:  "move brick",
    }

    // 创建Student类型变量,并根据字段在结构体中的顺序附初始值
    s5 := Student{
        20170901,
        "hzwy23",
        18,
        18107217021,
        "move brick",
    }
    fmt.Println("s1:", s1)
    fmt.Println("s2:", s2)
    fmt.Println("s3:", s3)
    fmt.Println("s4:", s4)
    fmt.Println("s5:", s5)
}

上边示例中可以看出,定义结构体变量通常有两种方式:第一种是使用new关键字创建指针类型变量,如上边示例代码中的变量s1;第二种是直接在结构体名称后边加上大括号({}),如上边示例代码中的变量s3。

通过在结构体后边加大括号({})的方式创建变量与使用new关键字创建对象不同之处在于new创建出来的是指针类型,而结构体名加大括号({})创建出来的不是指针类型。如果在使用结构体名加大括号({})创建变量时,在结构体名前边加上地址操作符(&),那么这样创建出来的也是指针类型变量,效果就与new关键字一样了,如上边示例代码中,s1与s2都是指针类型变量。

怎么操作结构体中的字段呢?

在程序中,可以通过结构体类型变量名加上点号(.),再加上字段名的方式访问结构体中的字段。如访问学生姓名的操作是:

s1.Name = "hi hzwy23"
s3.Name = "hello hzwy23"
fmt.Println(s3.Name)
fmt.Println(s1.Name)

不管变量是指针类型,还是非指针类型,操作结构体字段,都是一样的方法,如上边示例中s1是指针类型,s3是非指针类型,在访问字段名为Name的字段时,都是采用点号(.)加上字段名的方式访问。这一点与C/C++语言不同。

匿名字段

在定义struct中字段时,通常都是先写字段名,然后在后边跟上字段类型,这种方式定义的字段是常规字段。还有一种字段,叫做匿名字段,就是在结构体中,只有类型没有名字的字段。那么匿名字段的语法格式是什么呢?

type name struct{
    dataType
    dataType
    ...
}

哪些类型可以当做匿名字段使用呢?

golang中所有的类型都可以当做匿名字段使用。当struct出现了匿名字段后,可以理解成这个字段的名字就是类型名称,如定义一个int类型命名字段格式

type name struct{
    int
}

匿名字段可以理解为下边的形式:

type name struct{
    int  int
}

相当于结构体中有一个int类型的字段,字段名称是int。

匿名字段没有名字,怎么访问呢?

下面通过一段示例代码讲解匿名字段访问方法:

package main

import "fmt"

type name struct {
    // int类型匿名字段
    int
}

func main() {
    n := &name{
        int: 8,
    }
    fmt.Println(n.int)
}

上边的示例代码中,首先定义结构体name类型的变量n,通过n.int,也就是变量名加上点号(.),再加上类型名的方式访问了匿名字段。

上述示例讲解了匿名字段是内置基本类型时的情况,那么当匿名字段是自定义结构体类型时,会是什么样的情况呢?
当匿名字段是结构体时,那么这个匿名字段的默认名字就是这个结构体的名字。与内置基本类型不同的是,当匿名字段是结构体时,这个匿名字段中所有的信息将会传递给父级结构体。请看下边一段示例:

package main

import (
    "fmt"
)

// 学生基础信息
type Student struct {
    // 学号
    Id int
    // 学生姓名
    Name string
    // 学生年龄
    Age int
    // 手机号
    Mobile int
    // 专业
    Major string

    int
}

type Demo struct {
    Name string
    Student
}

func main() {

    // 创建Student类型变量,并根据字段在结构体中的顺序附初始值
    s := Student{
        20170901,
        "hzwy23",
        18,
        18107217021,
        "move brick",
        18,
    }
    de := &Demo{
        Name:    "test demo",
        Student: s,
    }
    fmt.Println("demo name is:", de.Name)
    // 指定匿名字段类型访问匿名字段中的信息
    fmt.Println("student name is:", de.Student.Name)
    // 不指定匿名字段类型,直接访问匿名字段中的信息
    fmt.Println("student id is:", de.Id)
    fmt.Println("student int is:", de.int)
}

输出信息是:

demo name is: test demo
student name is: hzwy23
student id is: 20170901
student int is: 18

Student是Demo中的匿名字段,Demo类型的变量在访问Student中的字段时,如访问学生名字字段的方法是 de.Student.Name,访问学生信息中int类型匿名字段方法是:de.int。也就是说,外层结构体可以直接访问匿名字段内部的全部信息,当然如果匿名字段内的非导出字段或方法,只能在同一个包中被访问。

既然匿名字段的名字是类型名称,那么自然而然一个结构体重,不允许出现两个类型一样的匿名字段。如下边一个错误的示例:

// 错误示例:
type name struct {
    int
    int
}

匿名字段内部冲突问题

当结构体A和结构体B都拥有Name这个字段时,在结构体C中使用结构体A和B作为匿名字段,那么C中就会出现两个Name,这样会不会冲突呢?请看下边示例:

package main

import "fmt"

type A struct {
    ID   string
    Name string
}

type B struct {
    ID   string
    Name string
}

type C struct {
    A
    B
    ID string
}

func main() {
    a := A{
        ID:   "AAA",
        Name: "struct A",
    }
    b := B{
        ID:   "BBB",
        Name: "struct B",
    }
    c := C{
        A:  a,
        B:  b,
        ID: "ccc",
    }
    fmt.Println(c.A.Name)
    fmt.Println(c.B.Name)
    fmt.Println(c.ID)

    //ambiguous selector c.Name
    //fmt.Println(c.Name)
}

输出信息:

struct A
struct B
ccc

结构体C中接收了A中的Name,也接收了B中的Name,如果使用结构体C的变量c直接来访问Name,如 c.Name,那么到底访问的是A中的字段Name,还是访问的B中的字段Name?在编译时会出现如下错误:

ambiguous selector c.Name

由于访问字段不明确,所以直接导致编译错误。那么对于这种情况,该怎么进行操作呢?将上边访问Name字段的语句换成下边情况:

fmt.Println(c.A.Name)
fmt.Println(c.B.Name)

输出信息是:

struct A
struct B

通过在变量c和字段Name之间加上匿名字段类型名,就可以解决上述字段访问模糊不清的情况。

另外3个结构体中都有名称为ID的字符串类型字段,golang会首先访问最外层的ID字段,也就是读取结构体C中的ID字段。如果外层没有ID字段,则情况就与Name字段相同了,必须显示的指定具体的匿名字段类型名,然后根据匿名字段类型名,访问下边的字段。

猜你喜欢

转载自blog.csdn.net/hzwy23/article/details/79922973
今日推荐