news 2026/6/25 23:19:34

Go进阶之理解方法本质

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Go进阶之理解方法本质

Go语言虽然不支持经典的面向对象的语法元素.比如继承 对象和类.Go语言也有方

法.和函数相比就是在声明形式上多了一个参数.Go称为receiver参数.receiver是参

数与类型之间的纽带.

方法声明格式:

func (receiver T/* T) MethodName(参数列表) (返回值列表){ //方法体 }

方法声明的T称为receiver的基类型.通过receiver.上述方法被绑定到类型T上.伪代码如下:

var t T t.methodName(参数列表) var pt *T = &t pt.methodName(参数列表)

    1.Go方法特点:

    1).方法名的首字母是否大写决定了该方法是不是导出方法.

    2).方法定义要与类型定义放在同一个包内.由此可以知道不能为原生类型(如int

    float64 map等)添加方法.只能为自定义类型定义方法.示例如下.

    type MyInt int64 func (i MyInt) String() string { return "自定义类型的方法" }

    同理可以得出不能跨域Go包为其他包内的自定义类型定义方法.

    3).每个方法只能有一个receiver参数.不支持多receiver参数列或变长receiver参

    数.一个方法只能绑定一个基类型.Go语言不支持同时绑定多个类型的方法.

    4).receiver参数的基类型本身不能是指针类型或者接口类型.示例如下.

    2.方法的本质:

    Go语言没有类.方法与类别通过reveiver联系到一起,可以为任何非内置原生类型定

    义方法.示例如下:

    type T struct { a int } func (t T) get() int { return t.a } func (t *T) set(a int) { t.a = a }

    receiver其实是作为第一个参数传入方法列表中.上述的方法可以等价转换为下面的

    函数,代码如下:

    func Get(t T) int { return t.a } func Set(t *T, a int) { t.a = a }

    这种转换后的函数就是方法的原型.只不过在Go语言中.这种等价转换是由Go编译器

    在编译和生成代码时自动完成的.Go语言规范中提供了一个新的概念.可以更充分理解

    等价转换.

    Go方法一般使用方式:

    func Test() { var t T t.get() t.set(1000) }

    等价替换:

    func ChangeTest() { var t T T.get(t) (*T).set(&t, 1000) }

    这种直接以类型名T调用方法的表达式被称为方法表达式.类型T只能调用T的方法集

    合(Method Set)中的方法.同理.*T只能调用*T的方法集合中的方法.这种通过方法表

    达式进行调用的方式与我们之前所做的方法到函数的等价转换如出一辙.这就是Go方

    法的本质.一个方法所绑定类型实例为第一个参数的普通函数.

    Go方法本身就是一个普通函数.我们甚至可以把它作为右值赋值给函数类型的变量.

    func Test() { var t T f1 := (*T).set f2 := T.get f1(&t,1) fmt.Println(f2(t)) }

    3.选择正确的reveiver类型:

    方法和函数的等价变换公式:

    func(t T) M1()<=> M1(t T) func(t *T) M2()<=> M2(t *T)

    M1方法的receiver参数类型为T.M2方法的receiver参数类型为*T.

    1).receiver类型为T时:

    选择以T作为receiver参数类型时.T的M1方法等价于M1(t T).Go函数的参数采用的

    是值复制传递.也就是说M1函数体中的t是T类型的一个副本.这样在M1函数实现中对

    参数t做任何修改都只会影响副本.不会影响到原T类型.

    2).receiver类型为*T时:

    选择以*T作为receiver参数类型时.T的M2方法等价为M2(t *T).我们传递给M2函

    数的t是T类型实例的地址.这样M2函数体中对参数t做的任何修改都会反应到原T类

    型实例上.

    示例如下:

    func main() { //t.a=0 var t T fmt.Println(t.a) t.M1() fmt.Println(t.a) t.M2() fmt.Println(t.a) } type T struct { a int } func (t T) M1() { t.a = 10 } func (t *T) M2() { t.a = 11 }

    执行结果:

    疑惑:是不是T类型实例只能调用receiver为T的类型方法.不能调用receiver为*T类

    型的方法呢.答案是否定.无论T类型实例还是*T类型实例.都既可以调用receiver为T

    类型的方法.也可以调用receiver为*T类型的方法.示例如下:

    func main() { var t T var pt = &T{} t.M1() t.M2() pt.M1() pt.M2() }

    可以看到上述都可以进行互相调用.实际上这都是Go语法糖.Go编译器在编译和生成

    代码时为我们做了自动转换.

    总结:

    如果要对类型实例进行修改.可以为receiver选择*T类型.

    如果没有对类型实例修改的需求.可以为receiver选择T类型或*T类型均可.如果类型

    的size过大的话.选择指针会更好一些.

    微笑着孤言寡语.

    如果大家喜欢我的分享的话.可以关注我的微信公众号

    念何架构之路

    版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
    网站建设 2026/6/15 7:23:34

    IntelliJ IDEA 全局搜索完全指南:从高效使用到快捷键失效排查

    前言 在现代软件开发中&#xff0c;代码库规模日益庞大&#xff0c;快速定位关键逻辑、变量定义或配置项已成为开发者的核心能力。IntelliJ IDEA 作为业界领先的 Java IDE&#xff08;同时也支持 Kotlin、Python、JavaScript 等多语言&#xff09;&#xff0c;其全局搜索&…

    作者头像 李华
    网站建设 2026/6/21 7:43:08

    强烈安利专科生必用9款一键生成论文工具测评

    强烈安利专科生必用9款一键生成论文工具测评 为什么需要一份权威的论文写作工具测评 随着学术研究的日益繁重&#xff0c;专科生在撰写论文过程中常常面临时间紧张、资料查找困难、格式不规范等问题。而AI写作工具的出现&#xff0c;为这一难题提供了新的解决方案。为了帮助专科…

    作者头像 李华
    网站建设 2026/6/25 20:31:49

    YOLO26改进策略【Backbone/主干网络】| ICLR-2023 替换骨干网络为:RevCol 一种新型神经网络设计范式

    一、本文介绍 本文记录的是基于RevCol的YOLO26目标检测骨干网络改进方法研究。 RevCol是一种新型神经网络设计范式,它由多个子网(列)及多级可逆连接构成,正向传播时特征逐渐解缠结且保持信息。可逆变换借鉴可逆神经网络思想,设计多级可逆单元用于解决模型对特征图形状的…

    作者头像 李华
    网站建设 2026/6/25 4:09:14

    CSS - code

    CSS code 倾斜按钮<style>button {width: 180px;height: 80px;background: #409eff;border: none;outline: none;display: block;margin: 0 auto;color: #fff;font-size: 18px;border-radius: 15px 0;position: relative;transform: skew(-20deg);}button::before {posit…

    作者头像 李华
    网站建设 2026/6/25 4:09:22

    删除某一个大表中的大部分数据

    场景 要删除表T中的大部分数据&#xff0c;因表太大&#xff0c;删除慢&#xff0c;得到另一种更加快速方法 方法 -- 1. 创建新表&#xff08;保留不需要删除的数据&#xff09; CREATE TABLE T_new LIKE T;-- 2. 插入保留数据&#xff08;假设需保留imei不在列表中的数据&…

    作者头像 李华