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

相关推荐
于顾而言14 小时前
【笔记】Go Coding In Go Way
后端·go
qq_1728055914 小时前
GIN 反向代理功能
后端·golang·go
follycat21 小时前
2024强网杯Proxy
网络·学习·网络安全·go
OT.Ter1 天前
【力扣打卡系列】单调栈
算法·leetcode·职场和发展·go·单调栈
探索云原生1 天前
GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU
ai·云原生·kubernetes·go·gpu
OT.Ter1 天前
【力扣打卡系列】移动零(双指针)
算法·leetcode·职场和发展·go
码财小子2 天前
k8s 集群中 Golang pprof 工具的使用
后端·kubernetes·go
明月看潮生5 天前
青少年编程与数学 02-003 Go语言网络编程 04课题、TCP/IP协议
青少年编程·go·网络编程·编程与数学
明月看潮生5 天前
青少年编程与数学 02-003 Go语言网络编程 03课题、网络编程协议
青少年编程·go·网络编程·编程与数学
帅气的人1236 天前
thrift idl 语言基础学习
java·开发语言·python·rpc·go·thrfit