Go面向对象详解

今天我们来了解一下 Go 中函数的另一种形态,带有接收者的函数,我们称为 method

method 是附属在一个给定的类型上的,他的语法和函数的声明语法几乎一样,只是在 func 后面增加了一个 receiver (也就是 method 所依从的主体)。

用 Rob Pike 的话来说就是:

"A method is a function with an implicit first argument, called a receiver."

method 的语法如下:
func (r ReceiverType) funcName(parameters) (results)

例如下面的 method 来实现面积的求和:

Go 复制代码
package main

import (
    "fmt"
)

type Rectangle struct {
    width, height float64
}

// 这样就实现了method
func (r Rectangle) area() float64 {
    return r.width * r.height
}

func main() {
    r1 := Rectangle{12, 2}
    r2 := Rectangle{9, 4}

    fmt.Println("Area of r1 is: ", r1.area())
    fmt.Println("Area of r2 is: ", r2.area())
}

使用 method 有下面的几个特点:

  1. 虽然 method 的名字一模一样,但是如果接收者不一样,那么 method 就不一样。
  2. method 里面可以访问接收者的字段。
  3. 调用 method 通过 . 访问,就像 struct 里面访问字段一样。

Receiver 还可以是指针,两者的差别在于,指针作为 Receiver 会对实例对象的内容发生操作,而普通类型作为 Receiver 仅仅是以副本作为操作对象,并不对原实例对象发生操作。

后文对此会有详细论述。

method 只能作用在 struct 上面吗? 当然不是,他可以定义在任何你自定义的类型、内置类型、struct 等各种类型上面。

struct 只是自定义类型里面一种比较特殊的类型而已,还有其他自定义类型申明,可以通过如下这样的申明来实现。

type typeName typeLiteral

请看下面这个申明自定义类型的代码。

Go 复制代码
type ages int
type money float32
type months map[string]int

实际上只是一个定义了一个别名,有点类似于 c 中的 typedef,例如上面 ages 替代了 int

指针作为 receiver

Go 复制代码
package main

import "fmt"

type Tree struct {
    Value int
    Left  *Tree
    Righ  *Tree
}

func (t Tree) setValue1(v int) {
    t.Value = v
}
func (t *Tree) setValue2(v int) {
    t.Value = v
}

func main() {
    t1 := Tree{
        Value: 0,
        Left:  nil,
        Righ:  nil,
    }
    t1.setValue1(1)
    fmt.Println(t1) // {0 <nil> <nil>} 并没有修改原来的值
    t1.setValue2(2)
    fmt.Println(t1) // {2 <nil> <nil>} 对原来的Tree修改了

    t2 := &Tree{
        Value: 0,
        Left:  nil,
        Righ:  nil,
    }
    t2.setValue1(1)
    fmt.Println(t2) // &{0 <nil> <nil>} // 并没有对原来的值进行修改
    t2.setValue2(2)
    fmt.Println(t2) // &{2 <nil> <nil>}
}

从上面的例子可以看出来:

  1. 如果一个 methodreceiver*T, 你可以在一个 T 类型的实例变量 V 上面调用这个 method,而不需要 &V 去调用这个 method

  2. 如果一个 methodreceiverT,你可以在一个 T 类型的变量 P 上面调用这个 method,而不需要 *P 去调用这个 method

这种特性:让开发者不必关注,调用的指针的 method 还是不是指针的 method,Go 能推断出你的目的,这对于有多年 C/C++ 编程经验的同学来说,真是解决了一个很大的痛苦。

如果你的方法想修改值就将 receiver 改为指针类型,否则就使用实例变量类型。

method 继承

如果匿名字段实现了一个 method,那么包含这个匿名字段的 struct 也能调用该 method

Go 复制代码
package main

import "fmt"

type Human struct {
    name  string
    age   int
    phone string
}

type Student struct {
    Human  // 匿名字段
    school string
}

type Employee struct {
    Human   // 匿名字段
    company string
}

// 在 human 上面定义了一个 method
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

func main() {
    mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}

    mark.SayHi()
    sam.SayHi()
}

method 重写 上面的例子中,如果 Employee 想要实现自己的 SayHi, 怎么办?

简单,和匿名字段冲突一样的道理,我们可以在 Employee 上面定义一个 method,重写了匿名字段的方法。

Go 复制代码
package main
import "fmt"

type Human struct {
    name  string
    age   int
    phone string
}

type Student struct {
    Human  // 匿名字段
    school string
}

type Employee struct {
    Human   // 匿名字段
    company string
}

// Human 定义 method
func (h *Human) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

// Employee 的 method 重写 Human 的 method
func (e *Employee) SayHi() {
    fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
		e.company, e.phone) //Yes you can split into 2 lines here.
}

func main() {
    mark := Student{Human{"Mark", 25, "222-222-YYYY"}, "MIT"}
    sam := Employee{Human{"Sam", 45, "111-888-XXXX"}, "Golang Inc"}

    mark.SayHi()
    sam.SayHi()
}

上面的代码设计的是如此的美妙,让人不自觉的为 Go 的设计惊叹!

通过这些内容,我们可以设计出基本的面向对象的程序了。

但是 Go 里面的面向对象是如此的简单,没有任何的私有、公有关键字,通过大小写来实现 (大写开头的为公有,小写开头的为私有),方法也同样适用这个原则。

相关推荐
烛阴1 天前
Go 语言进阶:打造可复用的模块,导出你的专属包
后端·go
gopher_looklook3 天前
从零到一: 用Go语言搭建简易RPC框架并实践 (一)
后端·go
猫九森4 天前
go 循环处理无限极数据
数据结构·后端·go
川Princess5 天前
【后端开发】字节跳动青训营Cloudwego脚手架
go·字节跳动青训营·cwgo
Pandaconda6 天前
【Golang 面试题】每日 3 题(四十三)
开发语言·经验分享·笔记·后端·面试·golang·go
川Princess8 天前
【后端开发】字节跳动青训营之性能分析工具pprof
go·字节跳动青训营·bytedance
用户2237209117729 天前
Go微服务精讲:Go-Zero全流程实战即时通讯
go
嘿嘿9 天前
Grafana 快速搭建go-metrics 仪表盘备忘
后端·docker·go
烛阴10 天前
Go 语言进阶必学:&^ 操作符,高效清零的秘密武器!
后端·go
Pandaconda10 天前
【Golang 面试题】每日 3 题(四十一)
开发语言·经验分享·笔记·后端·面试·golang·go