Go语言自学十八式:结构体和继承篇

对于数组与切片,只能存储同一类型的变量。若要存储多个类型的变量,就需要用到可将多种类型的变量组合在一起的聚合数据类型——结构体。每个变量是该结构体的成员变量。

Go语言 的结构体 struct 和其他面向对象语言的class(Go语言中没有,也就没有继承),但是Go语言放弃大量面向对象的特性。但同时Go语言满足:除指针类型外,所有的类型都可以有自己的方法。

定义结构体

结构体的声明

type 结构体名 struct {
    属性名   属性类型
    属性名   属性类型
    ...
}

比如要定义一个存储个人资料的名为 User 的结构体,可以这么写

type User struct {
    name   string
    age    int
    gender string
    mother *User // 指针
    father *User // 指针
}

定义方法(值传递)

在 Go 语言中,无法在结构体内定义方法,但是可以使用组合函数的方式来定义结构体方法。

它和普通函数的定义方式有些不一样,比如:

func (person User) FmtUser() {
    fmt.Printf("名字:%s\n", person.name)
    fmt.Printf("年龄:%d\n", person.age)
    fmt.Printf("性别:%s\n", person.gender)
}

其中 FmtUser 是方法名,而(person User) :表示将 FmtUser  方法与 User 的实例对象 person 绑定。

FmtUser 称为方法的接收者

person 表示实例对象本身,

在方法内可以使用 person.属性名 的方法来访问实例属性。完整代码如下:

package main

import "fmt"

// 定义一个名为User 的结构体
type User struct {
    name   string
    age    int
    gender string
    mother *User // 指针
    father *User // 指针
}

// 定义一个与 User 的绑定的方法
func (person User) FmtUser() {
    fmt.Printf("名字:%s\n", person.name)
    fmt.Printf("年龄:%d\n", person.age)
    fmt.Printf("性别:%s\n", person.gender)
}

func main() {
    // 实例化
    myself := User{name: "小明", age: 24, gender: "male"}
    // 调用函数
    myself.FmtUser()
}

输出如下:
名字:小明
年龄:24
性别:male

定义方法(指针传递)

当你想要在以上面方式定义的方法内改变实例的属性的时候,必须使用指针做为方法的接收者:

package main

import "fmt"

// 声明一个 User 的结构体
type User struct {
    name   string
    age    int
    gender string
    mother *User // 指针
    father *User // 指针
}

// 重点在于这个星号: *
func (person *User) increase_age() { 
    person.age += 1
}

func main() {
    myself := User{name: "小明", age: 24, gender: "male"}
    fmt.Printf("当前年龄:%d\n", myself.age)
    myself.increase_age()
    fmt.Printf("当前年龄:%d", myself.age)
}

输出如下:
当前年龄:24
当前年龄:25

可以看到在方法内部对 age 的修改已经生效。尝试去掉 *,使用值做为方法接收者,age则不会发生改变。

至此,两种定义方法的方式就有了:

  • 以值做为方法接收者
  • 以指针做为方法接收者

不管你使用哪种方法定义方法,以值或指针做为接收者都可以,都是可以调用的,Go语言中没有什么约束。

但是在以下的情况中,最好使用指针做为方法的接收者:

  • 需要在方法内部改变结构体内容的时候,
  • 出于性能的问题,当结构体过大的时候,使用指针做为方法的接收者

同时考虑代码一致性,建议全部情况都使用指针作为方法的接受者。

结构体实现 “继承”

Go 语言本身并不支持继承(所以加了引号)。但可以使用组合的方法,实现类似继承的效果。

在生活中,比如一台电脑,是由机身外壳,主板,CPU,内存等零部件组合在一起。

而在 Go 语言中,把一个结构体嵌入到另一个结构体的方法,称之为组合。

举个栗子:有一个表示公司(company)的结构体和一个表示公司职员(staff)的结构体,将公司信息与公司职员关联起来。

如果将 company 结构体的内容照抄到 staff 里。在实现上并没问题,但对同一公司的多个 staff 初始化时,会重复初始化相同的公司信息。

因此,借鉴继承的思想,可以将公司的属性都“继承”过来。而在 Go 中没有类的概念,只有组合。

type company struct {
    companyName string
    companyAddr string
}

type staff struct {
    name string
    age int
    gender string
    position string
}

组合允许将 company 这个结构体嵌入到 staff 中,做为 staff 的一个匿名字段,staff 就直接拥有了 company 的所有属性了:

type staff struct {
    name string
    age int
    gender string
    position string
    company   // 匿名字段 
}

完整代码如下 :

package main

import "fmt"

type company struct {
    companyName string
    companyAddr string
}

type staff struct {
    name string
    age int
    gender string
    position string
    company
}

func main()  {
    myCom := company{
        companyName: "Tencent",
        companyAddr: "深圳市南山区",
    }
    staffInfo := staff{
        name:     "小明",
        age:      28,
        gender:   "男",
        position: "算法工程师",
        company: myCom,
    }

    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.companyName)
    fmt.Printf("%s 在 %s 工作\n", staffInfo.name, staffInfo.company.companyName)
}

输出如下:
小明 在 Tencent 工作
小明 在 Tencent 工作

可见staffInfo.companyName 和 staffInfo.company.companyName 的效果是一样的。

内部方法与外部方法

在 Go 语言中,函数名的首字母大小写非常重要,它被来实现控制对方法的访问权限。

  • 当方法的首字母为大写时,这个方法对于所有包都是Public,其他包可以随意调用

  • 当方法的首字母为小写时,这个方法是Private,其他包是无法访问的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

薛定谔的猫96

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值