『No4: Go 结构体』

golang-learning-four.png
4.png

大家好,我是谢伟,是一名程序员。

本专栏的主旨是:梳理 Golang 知识,力求从初级水平提升至中级水平。

同时将一些符合人性的编程经验和好的编程方法,分享给大家。

希望对大家有帮助。

本节的主题是:结构体

0 引入

Golang 中用来表示单一的数据类型可以使用 变量声明的方式:

比如:

var Number int
var Married bool
var Name string

同一数据类型的集合可以使用下面的变量声明方式:

var Infor map[string]string
var Names []string
var Number [10]int

如果需要表示不同的数据类型的集合那应该怎么处理?

结构体

结构体可以用来表示不同的数据类型的集合, 同时可以表示用户自定义类型。

  1. 定义及声明
    • 按顺序初始化
    • 按任意顺序初始化
    • 字段赋值
    • new 函数分配指针
  2. 标签
  3. 字段访问
  4. 方法:值传递和指针传递
  5. 函数和方法的区别
  6. 组合:内嵌结构体、内嵌匿名成员
  7. 格式化显示结构体

1. 定义及声明

typestruct 关键字构成结构体的声明。

在 Golang 中, 结构体可以当做是一种数据类型。

所以可以这样这样操作:

type Name struct{}
var Infor Name

另外,结构体在 golang 中的作用又可以当做类。

所以又存在方法。


type Name struct {
}

func (n Name) ResponseNumber() int {
    return 1
}

调用结构体方法是使用 点号.


func main() {
    var name Name
    name.ResponseNumber()
}

声明示例:

type Information struct {
    Name    string
    Age     int
    School  string
    Married bool
    Habits  []string
    Infor   map[string]string
}

func exampleInformationDeclare() {
    // one
    var (
        exampleInformationOne Information
        Infor                 map[string]string
    )
    Infor = make(map[string]string)
    Infor["key"] = "hello-world"
    exampleInformationOne = Information{
        "xiewei", 18, "shanghaiUniversity", true,
        []string{"1", "2", "3"}, Infor,
    }
    fmt.Println(exampleInformationOne, exampleInformationOne.Name)

    // two
    var exampleInformationTwo Information
    exampleInformationTwo.Name = "golang"
    exampleInformationTwo.School = "Google"
    exampleInformationTwo.Age = 10
    exampleInformationTwo.Infor = Infor

    fmt.Println(exampleInformationTwo, exampleInformationTwo.Married)

    // three
    var exampleInformationThree Information
    exampleInformationThree = Information{
        Name:    "Python",
        School:  "Gudio school",
        Age:     18,
        Habits:  []string{"1", "2"},
        Infor:   Infor,
        Married: true,
    }

    fmt.Println(exampleInformationThree, exampleInformationThree.School)

    // four

    var exampleInformationFour *Information
    exampleInformationFour = new(Information)
    exampleInformationFour.Name = "Java"

    fmt.Println(exampleInformationFour, exampleInformationFour.School)
}



func main() {
    exampleInformationDeclare()
}


2. 标签

这种带标签的结构体的定义,在转换为json格式的数据的时候会自动将对应的字段转换为标签中的字段:

比如:Name 转换为 name 字段。

标签字段不能在编程中实际使用。

比如:可以这样: InformationWithLabel.Name

不可以这样:InformationWithLabel.name

type InformationWithLabel struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

3. 字段的访问

结构体内的是一系列相关字段的集合。如果定义了一个结构体如何访问?

可以使用点号获取结构体的字段。

比如:

type InformationWithLabel struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func exampleInformationWithLabelFiled() {
    var example InformationWithLabel
    example = InformationWithLabel{
        Name: "xiewei",
        Age:  18,
    }
    fmt.Println(example.Name, example.Age)
}


4. 方法

上文讲到在 golang 中结构体相当于面向对象中的类。

所以存在方法。

方法根据传入的参数的不同,又分为:值传递 和 指针传递。两者的效果就是:值传递不可改变值,指针传递可以改变值。

示例:

type Response struct {
    Code    int
    Result  []byte
    Headers map[string]string
}

func (r Response) GetAttrCode() int {
    return r.Code
}
func (r Response) GetAttrResult() []byte {
    return r.Result
}

func (r Response) GetAttrHeader() map[string]string {
    return r.Headers
}

func (r *Response) SetCode(code int) {
    r.Code = code
}

func (r *Response) SetHeaders(key, value string) {
    r.Headers[key] = value
}

func exampleResponse() {
    var (
        response Response
        headers  map[string]string
    )
    headers = make(map[string]string)
    headers["Server"] = "GitHub.com"
    headers["Status"] = "Ok"
    response.Headers = headers
    response.Code = 200
    response.Result = []byte("hello world")

    fmt.Println(response.GetAttrCode())
    fmt.Println(response.GetAttrHeader())
    fmt.Println(response.GetAttrResult())

    response.SetCode(404)
    fmt.Println(response)

    response.SetHeaders("Status", "failed")
    fmt.Println(response)
}


func main() {
    exampleResponse()
}


>>>

200
map[Server:GitHub.com Status:Ok]
[104 101 108 108 111 32 119 111 114 108 100]
{404 [104 101 108 108 111 32 119 111 114 108 100] map[Server:GitHub.com Status:Ok]}
{404 [104 101 108 108 111 32 119 111 114 108 100] map[Status:failed Server:GitHub.com]}

可以看出:

  • 结构体的方法如何定义
  • 值传递的适用于取值
  • 指针传递适用于更改字段的值

同时 方法和普通的函数的定义又有些许的不同。

5. 函数和方法的区别

Golang 中函数和方法存在区别。

先看示例:

func NormalFunc(arg int) int {
    return arg + 1
}


func (r *Response) SetCode(code int) {
    r.Code = code
}

Go 方法是作用在接收者(receiver)上的一个函数。接收者可以是几乎任何类型。

但一般选择 结构体 作为接收者。

6. 组合:

匿名字段

在 Golang 中可以通过结构体的组合实现类的继承。

即:将一个结构体A当成另一个结构体B的匿名字段,则 这个结构体B自动拥有A的所有字段和方法。

type Response struct {
    Code    int
    Result  []byte
    Headers map[string]string
}

func (r Response) GetAttrCode() int {
    return r.Code
}
func (r Response) GetAttrResult() []byte {
    return r.Result
}

func (r Response) GetAttrHeader() map[string]string {
    return r.Headers
}

func (r *Response) SetCode(code int) {
    r.Code = code
}

func (r *Response) SetHeaders(key, value string) {
    r.Headers[key] = value
}

type Requests struct {
    Url    string
    Params string
}

type CollectionRequests struct {
    CollectionNumber int
    Requests
    Response
}

func exampleCollectionRequests() {

    var collectionRequests CollectionRequests
    collectionRequests.CollectionNumber = 10
    collectionRequests.Url = "https://www.example.com"
    collectionRequests.Params = "name"
    collectionRequests.Code = 201
    collectionRequests.Result = []byte("hello Golang")

    var headers map[string]string
    headers = make(map[string]string)
    headers["status"] = "Good"
    collectionRequests.Headers = headers
    fmt.Println(collectionRequests)
    
    fmt.Println(collectionRequests.GetAttrCode())
}

func main() {
    exampleCollectionRequests()
}

>>>

{10 {https://www.example.com name} {201 [104 101 108 108 111 32 71 111 108 97 110 103] map[status:Good]}}
201

上文CollectionRequests 拥有两个匿名字段Requests、Response ,则自动拥有这个两个结构体的字段和方法。

内嵌结构体

这个组合的形式会遇到两个问题:

  • 字段相同怎么办?即结构体A 有字段 a, 结构体 B 也有字段 a。怎么处理?
  • 方法相同怎么办?即结构体A 有方法 methodOne, 结构体 B 也有方法 methodOne。怎么处理?

应该尽量避免命名冲突。同时可以使用多个点号的方法访问字段。方法则优先使用结构体B 的。

type OtherRequests struct {
    Request Requests
    Resp    Response
    Code    int
}
func (o OtherRequests) GetAttrCode() {
    fmt.Println(fmt.Sprintf("Outer Code = %d", o.Code))
    fmt.Println(fmt.Sprintf("inner Code = %d", o.Resp.Code))
}
func exampleOtherRequests() {
    var other OtherRequests
    other.Code = 201
    other.Resp.Code = 202
    fmt.Println(other)
    other.GetAttrCode()
    fmt.Println(other.Resp.GetAttrCode())
}
func main() {
    exampleOtherRequests()
}


>>>
{{ } {202 [] map[]} 201}
Outer Code = 201
inner Code = 202
202

可以看出通过组合结构体的方式,可以实现类中的继承和多态。

7. 格式化显示结构体

为理解这一节,我们先看看 Python 中如何格式化显示Class

class Info(object):

    def __init__(self, name, age, school):
        self.Name = name
        self.Age = age
        self.School = school

    def __repr__(self):
        return "Name = {}, Age = {}, School = {} ".format(self.Name, self.Age, self.School)

    def __str__(self):
        return "Name = {}, Age = {}, School = {} ".format(self.Name, self.Age, self.School)

if __name__ == "__main__":
    A = Info("xieWei", 20, "shanghaiUniversity")
    print(A)

>>>

Name = xieWei, Age = 20, School = shanghaiUniversity 

同样在 Golang 中只需要实现一个名为 String 的方法即可格式化显示结构体。

type OtherRequests struct {
    Request Requests
    Resp    Response
    Code    int
}

func (o OtherRequests) String() string {
    return fmt.Sprintf("Request = %v , Response = %v , Code = %d", o.Request, o.Resp, o.Code)
}

func exampleOtherRequests() {
    var other OtherRequests
    other.Code = 201
    other.Resp.Code = 202
    fmt.Println(other)
    other.GetAttrCode()
    fmt.Println(other.Resp.GetAttrCode())
}

func main() {

    exampleOtherRequests()
}

>>>
Request = { } , Response = {202 [] map[]} , Code = 201
Outer Code = 201
inner Code = 202
202

本节结束。希望有所启发,再会。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容