golang学习系列——22. 结构体(struct)/类

目录

 

1. 说明

2. 结构体

2.1 定义

2.2 创建

2.3 初始化

2.4 使用

2.5 结构体转换

3. 结构体进阶

3.1 带标签的结构体

3.2 匿名字段和内嵌结构体

3.3 工厂模式


1. 说明

Go 通过类型别名和结构体的形式支持用户自定义类型,或者叫定制类型。

Go 语言可以为自定义类型定义方法, 来完成类似其他语言类的定义.

2. 结构体(类)

2.1 定义

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

结构体成员一般都有名字,如果字段在代码中从来也不会被用到,那么可以命名它为 _。

结构体成员类型可以是任意类型, 可以是结构体/函数或者接口.

注意:当成员类型是结构体自身的时候(如:链表/树), 必须使用指针类型.

类型 struct1 在定义它的包 pack1 中必须是唯一的,它的完全类型名是:pack1.struct1。

2.2 创建

使用 new 函数, 会给结构体变量分配内存, 并返回该类型的指针

var t *T
t = new(T)
//t := new(T)
// 等价于 t = &T{}

直接声明变量, 也会分配内存, 并初始化为零值

var t T

2.3 初始化

type struct1 struct {
    age int
    high float
    name string
}
ms := struct1{10, 12.3, "blue"} //必须按照定义中的顺序来初始化数据
ms2 := struct1{high:14.5, name:"red"} //对指定字段进行初始化, 未初始化的字段为零值

2.4 使用

var st struct1
//使用 . 来获取结构体成员, 结构体变量和结构体指针都用 .
//不用像 c/c++ 那样用 . 和 ->
st.name = "red"
fmt.Println(st.name)

2.5 结构体转换

当为结构体定义了一个 alias 类型时,此结构体类型和它的 alias 类型都有相同的底层类型, 我们可以进行类型转换

type number struct {
    a int
}

type num number //alias type

func main() {
    a := number{23}
    b := num{32}
    // var i float32 = b   // compile-error: cannot use b (type nr) as type float32 in assignment
    // var i = float32(b)  // compile-error: cannot convert b (type nr) to type float32
    // var c number = b    // compile-error: cannot use b (type nr) as type number in assignment
    // needs a conversion:
    var c = number(b)
    fmt.Println(a, b, c)
}

3. 结构体进阶

3.1 带标签的结构体

结构体中的字段除了名字和类型外, 还有一个可选的标签 (tag): 它是一个附属于字段的字符串, 可以是文档或其他标记.

标签内容只有包 reflect 能获取到它. 举例如下:

type TagType struct { // tags
    res  bool   "An important answer"
    name string "The name of the thing"
    num  int    "How much there are"
}

func refTag(tt TagType, ix int) {
    ttType := reflect.TypeOf(tt)
    ixField := ttType.Field(ix)
    fmt.Printf("%v\n", ixField.Tag)
}

func main() {
    tt := TagType{true, "red", 1}
    for i := 0; i < 3; i++ {
        refTag(tt, i)
    }
}

3.2 匿名字段和内嵌结构体(继承)

结构体可以包含一个或多个匿名/内嵌字段,

匿名字段: 这些字段没有显式的名字只有类型, 此时类型就是字段名字.

内嵌结构体: 匿名字段本身是结构体类型

Go 中通过内嵌或组合来实现类似于继承的特性.

type size struct {
    long int
    wide int
}

type AA struct {
    name string
    int
    size
}

func main() {
    test.Test()
    f := test.NewStudent(10, "red")
    f.Show()

    aa := new(AA)
    aa.name = "red"
    aa.int = 12

    //可以把内嵌结构体作为结构体使用
    //aa.size = size{10, 10}
    //也可以使用内嵌结构体内部的字段
    aa.long = 10
    aa.wide = 10

    fmt.Printf("%v\n", aa)
}

命名冲突:

结构体内两个字段拥有相同的名字(可能是继承来的名字)时:

1. 外层名字会覆盖内层名字(但是两者的内存空间都保留),这提供了一种重载字段或方法的方式;
2. 如果相同的名字在同一级别出现了两次,如果这个名字被程序使用了,将会引发一个错误(不使用没关系)。没有办法来解决这种问题引起的二义性,必须由程序员自己修正。

type (
    A struct {a int}
    B struct {a, b int}
    C struct {A; B}
    D struct {B; b float32}
)

func main() {
    var c C
    //使用 c.a 是错误的, 存在二义性
    //c.a = 12 
    fmt.Println(c)

    var d D
    //d.b 为外层的float32
    fmt.Println(reflect.TypeOf(d.b))
}

3.3 工厂模式

Go 不支持面向对象编程语言中那样的构造方法, 但是我们可以自己实现. 例如

type Student struct {
    age int
    name string
}

func NewStudent(age int, name string) *Student {
    if age < 0 {
        return nil
    }
    return &Student{age, name}
}

func main() {
    f := NewStudent(10, "red")
}

我们可以使用可见性规则来让类型私有化, 强制用户使用工厂类方法, 例如

package test

import (
    "fmt"
)

//外部不可以使用 new 函数来创建 student 实例, 也不可以直接使用 student 结构体
type student struct {
    age  int
    name string
}

//外部可以用实例使用可见方法
func (st *student) Show() {
    fmt.Println("i'm", st.name, ",i am", st.age, "years old!")
}

//外部使用工厂方法得到结构体实例
func NewStudent(age int, name string) *student {
    if age < 0 {
        return nil
    }
    return &student{age, name}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值