go语言学习-结构体

结构体

go语言中的结构体,是一种复合类型,有一组属性构成,这些属性被称为字段。结构体也是值类型,可使用new来建立。数组

定义:函数

type name struct {
    field1 type1
    field2 type2
    ...
}

咱们能够看到每个字段都由一个名字和一个类型构成,不过实际上,若是咱们若是不须要使用某个字段时,可使用”_”来代替它的名字学习

而且结构体字段能够是任意类型,函数,接口,甚至是结构体自己均可以this

使用结构体

定义一个Person结构体指针

type Person struct {
    name string
    age  int
}

使用code

// 字面量形式初始化
var p1 = Person{
    name: "Tom",
    age: 18,
}

var p2 = Person{"Cat", 20}

fmt.Println(p1)          //{Tom 18}                                                                                     
fmt.Println(p1.name)     // Tom
fmt.Println(p2)          // {Cat 20}

p1.age = 20      // 设置p的age字段的值
fmt.Println(p1)     // {Tom 20}
还可使用new函数来给一个结构体分配内存,并返回指向已分配内存的指针

var p3 *Person   // 声明一个Person类型的指针
p3 = new(Person)   // 分配内存

// 上面两句至关于
p3 := new(Person)

p3.name = "Cat"
p3.age = 10

fmt.Println(p3)       // &{Cat 10}
fmt.Println(p3.name)  //

咱们能够直接使用结构体指针经过”.”来访问结构体的字段,就像直接使用结构体实例同样, go会自动进行转换对象

还有一种叫作混合字面量的语法来声明,以下,这其实只是一种简写方式,底层仍是调用new方法继承

var p4 = &Person{"Dog", 10}  // 一样返回的是Person类型的指针

fmt.Println(p4)             // &{Dog 10}
fmt.Println(p4.name)        // Dog

匿名字段

go语言的结构体还支持匿名字段,也就是说一个只有类型而没有字段名(连”_”都没有)的字段,被匿名嵌入的也能够是任何类型,此时类型名就是字段的名字,也就是咱们能够直接使用类型名为字段名来访问匿名字段.接口

另外若是匿名字段是另外一个结构体,这就叫作内嵌结构体,这个特性能够模拟相似继承的行为。内存

type Person struct {
    name string
    age  int
}

type Student struct {
    Person
    int
}

// 定义一个Student类型的变量
var s = Student{Person{"gdb", 10}, 10}

// 可使用以下的方法访问内部结构体中的字段
fmt.Println(s.Person.name)

// 也能够这样访问,go将自动使用Person的name属性,不过若是在Student中也定义了name字段,这里就不能使用了
fmt.Println(s.name)

// 访问int类型的匿名字段,此时类型就是字段的名字
fmt.Println(s.int)

注意:这样若是两个字段有相同的名字时,外部的名字会覆盖内部的;若是同一级别出现相同名字的字段,会出错,须要注意;而且不能同时嵌⼊某⼀类型和其指针类型,由于它们名字相同。

标签

结构体中的字段除了能够有名称和类型之外,还能够有标签。它是一个附属于字段的字符串,能够是文档或其余的重要标记。后面说反射时再说。

方法

以前学习的面向对象语言,好比说Java, Python中,有类的概念,每一个类均可以有本身的成员变量,成员方法,它们都是定义在类中的

go语言中的结构体就相似与面向对象语言的类,而结构体的字段就至关于类中的成员变量,结构体也能够有方法,可是不是直接定义在结构体中的,go语言中有一个接收者的概念,咱们能够将函数做用在一个接收者,此时这个函数就被称为方法

接收者是某种类型的变量,不单单能够是结构体,几乎任何类型均可以是结构体,好比: int,bool, string或数组的别名类型,甚至能够是函数类型,不过不能是接口类型

定义方法的示例:

type Person struct {
    name string
    age int
}

// 使用Person类型的实例作接收者,这就是一个Person类型方法,方法名前面括号中的就是接收者
func (this Person) getName() string {
    return this.name
}

// Peron类型的指针对象也能够做为接收者
func (this *Person) setName(name string) {
    this.name = name
}

tom := Person{"Tom", 20}
fmt.Println(tom)  // {Tom 20}
fmt.Println(tom.getName())  // Tom

tom.changeName("Bob")
fmt.Println(tom)  // {Bob 20}

这里有一点须要注意:类型和绑定它的方法必须在同一个包中(不必定要在同一个文件中)

这里使用类型直接做为接收着 和 类型的指针做为接收者的区别,就至关于普通函数中,值类型的参数和引用类型参数的区别;即在方法中对类型的实例的操做,不会影响外部的实例的值,而使用类型指针的实例做为引用参数,在方法内部修改会影响外部的实例

匿名字段

咱们也可使用结构体内部的匿名字段,做为方法的接收者,此时这个结构体,仍然能够调用这个方法,此时编译器会负责查找

type Person struct {
    name string
    age int
}

type Student struct {
    Person
    score int
}

func (p *Person) show() {
    fmt.Println("My name is " + p.name + ", I'm " + strconv.Itoa(p.age) + " years old")
}


tom := Person{"Tom", 20}
// 调用匿名字段做为接收器的方法
tom.show()   // My name is Tom, I'm 20 years old

在此基础上,咱们还能够在结构体上,实现与匿名字段同名的方法,就像面向对象中的重写相似,编译器会先查找结构体实例做为接收器的方法。

方法集

根据定义结构体以及方法的不一样,方法集也有所不一样,了解他们,对理解接口有帮助

type T struct {
    name string
    age int
}

type G struct {
    T
    action string
}

type S struct {
    *T
    sel string
}
  • 类型 T 的方法集包含全部接收者为 T 的方法
  • 类型T的方法集包含全部接收者为 T的方法(由于go会自动解引用)和全部接收者为 T 的方法
  • 类型G包含匿名字段 T, 则G的方法集,仅仅包含T类型的方法集
  • 类型S包含匿名字段 *T,则S的方法集,包含T和*T类型的方法集
相关文章
相关标签/搜索