初学go时很多同学会把 值接收者 和 指针接收者 的方法相互调用搞混淆,好多同学都只记得指针类型可以调用值接收者方法指针接收者方法,而值类型只能调用值接收者方法,其实不然,在某些情况下,值类型也是可以调用指针接收者方法的。

最近又看到有同学发出了这样的疑问,所以打算记录一下,用以备忘、分享。

类型不同可以调用

package main

import (
"fmt"
) type field struct {
name string
} func (p *field) pointerMethod() {
fmt.Println(p.name)
} func (p field) valueMethod() {
fmt.Println(p.name)
} func main() {
fp := &field{name: "pointer"}
fv := field{name: "value"} fp.pointerMethod()
fp.valueMethod()
fv.pointerMethod()
fv.valueMethod()
}
//output: pointer pointer value value

首先我们给field定义了两个方法,一个是指针接收者的pointerMethod,一个是值接收者valueMethod

然后我们创建了变量,fp是指针类型,fv是值类型。

fpfv分别调用pointerMethodvalueMethod,可以看到他们都可以通过编译正常输出。

当类型和方法的接收者类型不同时,编译器会做一些操作:

值类型调用指针接收者方法时,实际为(&fv).pointerMethod()

指针类型调用值接收者方法时,实际为(*fp).valueMethod()

这里是类型不同可以相互调用的情况,再说下不能调用的情况。

类型不同不可以调用

不能调用的情况有两种:

  • 值类型不能被寻址
  • 指针接收者实现接口

两种情况都是值类型不能调用指针接收者方法

值类型不能被寻址

如果值类型实体不能被寻址,那么它就不能调用指针接收者方法

package main

import (
"fmt"
) type field struct {
name string
} func (p *field) pointerMethod() {
fmt.Println(p.name)
} func (p field) valueMethod() {
fmt.Println(p.name)
} func NewFiled() field {
return field{name: "right value struct"}
} func main() {
NewFiled().valueMethod()
NewFiled().pointerMethod()
}

运行代码报错:

./x.go:37:12: cannot call pointer method on NewFiled()
./x.go:37:12: cannot take the address of NewFiled()

看来编译器首先试着给 NewFoo() 返回的右值调用 pointer method,出错;然后试图给其插入取地址符,未果,就只能报错了。

至于左值和右值的区别,大家感兴趣可以自行搜索一下。大致来说,最重要区别就是是否可以被寻址,可以被寻址的是左值,既可以出现在赋值号左边也可以出现在右边;不可以被寻址的即为右值,比如函数返回值、字面值、常量值等等,只能出现在赋值号右边。

指针接收者实现接口

使用指针接收者实现接口方法,那么只有指针类型的实体实现了接口

package main

import "fmt"

type human interface {
speak()
sing()
} type man struct {
} func (m man) speak() {
fmt.Println("speaking")
} func (m *man) sing() {
fmt.Println("singing")
} func main() {
var h human = &man{}
h.speak()
h.sing()
}

上面代码可以正常编译输出,但是我们把&man{}修改为man{}就编译不过了

func main() {
var h human = man{}
h.speak()
h.sing()
}

报错如下:

./x.go:22:6: cannot use man literal (type man) as type human in assignment:
man does not implement human (sing method has pointer receiver)

man没有实现human,因为sing是个指针接收者方法。

这里看下飞雪无情的总结:

实体类型以指针接收者实现接口的时候,只有指向这个类型的指针才被认为实现了该接口

如果是值接收者,实体类型的值和指针都可以实现对应的接口;如果是指针接收者,那么只有类型的指针能够实现对应的接口

再看下饶大的解释:

接收者是指针类型的方法,很可能在方法中会对接收者的属性进行更改操作,从而影响接收者;而对于接收者是值类型的方法,在方法中不会对接收者本身产生影响。

所以,当实现了一个接收者是值类型的方法,就可以自动生成一个接收者是对应指针类型的方法,因为两者都不会影响接收者。但是,当实现了一个接收者是指针类型的方法,如果此时自动生成一个接收者是值类型的方法,原本期望对接收者的改变(通过指针实现),现在无法实现,因为值类型会产生一个拷贝,不会真正影响调用者。

平时我们写代码的时候也不用可以记这个,不仅编译器会报错,goland也一样会提示。

参考

https://www.qtmuniao.com/2020/01/06/go-value-pointer-method/

https://qcrao91.gitbook.io/go/interface/zhi-jie-shou-zhe-he-zhi-zhen-jie-shou-zhe-de-qu-bie

https://www.flysnow.org/2017/04/03/go-in-action-go-interface.html

golang指针接收者和值接收者方法调用笔记的更多相关文章

  1. Golang 方法接收者是值还是指针问题

    对于普通结构体作为接收者,值和指针并没有区别. (以下代码摘抄自Go In Action 中文版) type defaultMatcher struct{} // 方法声明为使用 defaultMat ...

  2. go中方法的接收者是值或者指针的区别

    值类型的变量和指针类型的变量 先声明一个结构体: type T struct { Name string } func (t T) M1() { t.Name = "name1" ...

  3. atitit.架构设计---方法调用结果使用异常还是返回值

    atitit.架构设计---方法调用结果使用异常还是返回值 1. 应该返回BOOL类型还是异常 1 2. 最终会有四种状况,抛出异常.返回特殊值.阻塞.超时 1 3. 异常的优缺点点 1 4. jav ...

  4. Java加载jar文件并调用jar文件当中有参数和返回值的方法

    在工作当中经常遇到反编译后的jar文件,并要传入参数了解其中的某些方法的输出,想到Java里面的反射可以实现加载jar文件并调用其中的方法来达到自己的目的.就写了个Demo代码. 以下的类可以编译生成 ...

  5. JAVA方法调用中的解析与分派

    JAVA方法调用中的解析与分派 本文算是<深入理解JVM>的读书笔记,参考书中的相关代码示例,从字节码指令角度看看解析与分派的区别. 方法调用,其实就是要回答一个问题:JVM在执行一个方法 ...

  6. 深入解析多态和方法调用在JVM中的实现

    深入解析多态和方法调用在JVM中的实现 1. 什么是多态 多态(polymorphism)是面向对象编程的三大特性之一,它建立在继承的基础之上.在<Java核心技术卷>中这样定义: 一个对 ...

  7. golang中的左值VS右值

    对应关系 左值 可寻址 右值 不可寻址 可寻址:可以通过&取地址符,获取内存地址; 可寻址,也就是分配了内存; 不可寻址:根本没有分配内存; 常量const 常量通常只支持数字/字符串/布尔, ...

  8. 图解JVM执行引擎之方法调用

    一.方法调用 方法调用不同于方法执行,方法调用阶段的唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.Class文件的编译过程中不包括传统编译器中的连接步骤,一 ...

  9. [Effective JavaScript 笔记]第18条:理解函数调用、方法调用及构造函数调用之间的不同

    面向对象编程中,函数.方法.类的构造函数是三种不同的概念. JS中,它们只是单个构造对象的三种不同的使用模式. 三种不同的使用模式 函数调用 function hello(username){ ret ...

随机推荐

  1. web浏览器知识点

    网页是怎么形成的 前端的代码(英文字母)---->浏览器渲染 ------- >  客户眼中的效果 浏览器(显示代码) 游览器是网页显示,运行的平台,常用的的游览器有IE(Edge).火狐 ...

  2. Linux下使用pure-ftpd建立匿名ftp访问

    by 无若 (一)ubuntu14.04下使用pure-ftpd建立匿名ftp访问 1.安装apt-get install pure-ftpd 2.修改配置nano /etc/pure-ftpd/co ...

  3. 配置SSH公钥以及创建远程仓库

    一.配置SSH公钥 1.生成SSH公钥 在我们自己电脑的桌面上右键菜单,打开git命令行,输入以下命令: ssh-keygen -t rsa 一直敲回车之后,显示以下信息即表示成功生成SSH公钥,并且 ...

  4. Openresty Lua协程调度机制

    写在前面 OpenResty(后面简称:OR)是一个基于Nginx和Lua的高性能Web平台,它内部集成大量的Lua API以及第三方模块,可以利用它快速搭建支持高并发.极具动态性和扩展性的Web应用 ...

  5. java中sort方法的自定义比较器写法(转载)

    java中sort方法的自定义比较器写法 摘要 在做一些算法题时常常会需要对数组.自定义对象.集合进行排序. 在java中对数组排序提供了Arrays.sort()方法,对集合排序提供Collecti ...

  6. C++ 基于STL的演讲比赛流程管理系统(sort算法+小型算法(accumulate)+内建函数对象+string字符串拼接+字符串截取+多个容器基础操作+与用户交互+文件的读写+保存+重建+整体文件数据的清空)

    1 /* 2 比赛规则: 3 学校举行一演讲比赛,共12个人参加,比赛两轮,第一轮为淘汰赛 第二轮为决赛 4 每名选手都有对应的编号:如10001~10012 5 比赛方式:分组比赛 每组6人 6 第 ...

  7. Android WorkManager使用入门

    WorkManager使用入门 WorkManager提供了任务调度功能,我们可以对工作进行标记或命名. 我们用一个示例来演示如何使用WorkManager.本文使用Kotlin. 入门示例 grad ...

  8. (一)Linux之目录结构

    Linux之目录结构 目录 Linux之目录结构 一.概述 一.基本介绍 二.具体的目录结构(不用背,知道即可) Linux /usr目录 Linux /var 目录 一.概述 学习 Linux,不仅 ...

  9. 算法入门 - 动态数组的实现(Java版本)

    静态数组 Java中最基本的数组大家肯定不会陌生: int[] array = new int[6]; for (int i = 0; i < array.length; i++){ array ...

  10. 题解 P6271 [湖北省队互测2014]一个人的数论

    通过这道题学了伯努利数,写篇题解推一下 题目 先推一下式子 \[\sum_{i=1}^ni^d[gcd(i,n)=1] \] \[\sum_{i=1}^{n}i^d\sum_{k|i}\sum_{k| ...