值类型的变量和指针类型的变量

先声明一个结构体:

type T struct {
Name string
} func (t T) M1() {
t.Name = "name1"
} func (t *T) M2() {
t.Name = "name2"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

M1() 的接收者是值类型 T, M2() 的接收者是值类型 *T , 两个方法内都是改变Name值。

下面声明一个 T 类型的变量,并调用 M1() 和 M2() 。

    t1 := T{"t1"}

    fmt.Println("M1调用前:", t1.Name)
t1.M1()
fmt.Println("M1调用后:", t1.Name) fmt.Println("M2调用前:", t1.Name)
t1.M2()
fmt.Println("M2调用后:", t1.Name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果为:

M1调用前: t1
M1调用后: t1
M2调用前: t1
M2调用后: name2
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

下面猜测一下go会怎么处理。

先来约定一下:接收者可以看作是函数的第一个参数,即这样的: func M1(t T)func M2(t *T)。 go不是面向对象的语言,所以用那种看起来像面向对象的语法来理解可能有偏差。

当调用 t1.M1() 时相当于 M1(t1) ,实参和行参都是类型 T,可以接受。此时在M1()中的t只是t1的值拷贝,所以M1()的修改影响不到t1。

当调用 t1.M2() => M2(t1),这是将 T 类型传给了 *T 类型,go可能会取 t1 的地址传进去: M2(&t1)。所以 M2() 的修改可以影响 t1 。

T 类型的变量这两个方法都是拥有的。


下面声明一个 *T 类型的变量,并调用 M1() 和 M2() 。

    t2 := &T{"t2"}

    fmt.Println("M1调用前:", t2.Name)
t2.M1()
fmt.Println("M1调用后:", t2.Name) fmt.Println("M2调用前:", t2.Name)
t2.M2()
fmt.Println("M2调用后:", t2.Name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果为:

M1调用前: t2
M1调用后: t2
M2调用前: t2
M2调用后: name2
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

t2.M1() => M1(t2), t2 是指针类型, 取 t2 的值并拷贝一份传给 M1。

t2.M2() => M2(t2),都是指针类型,不需要转换。

*T 类型的变量也是拥有这两个方法的。

传给接口会怎样?

先声明一个接口

type Intf interface {
M1()
M2()
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

使用:

    var t1 T = T{"t1"}
t1.M1()
t1.M2() var t2 Intf = t1
t2.M1()
t2.M2()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

报错:

./main.go:9: cannot use t1 (type T) as type Intf in assignment:
T does not implement Intf (M2 method has pointer receiver)
  • 1
  • 2
  • 1
  • 2

var t2 Intf = t1 这一行报错。

t1 是有 M2() 方法的,但是为什么传给 t2 时传不过去呢?

简单来说,按照接口的理论:传过去【赋值】的对象必须实现了接口要求的方法,而t1没有实现M2(),t1的指针实现了M2()。另外和c语言一样,函数名本身就是指针

当把 var t2 Intf = t1 修改为 var t2 Intf = &t1 时编译通过,此时 t2 获得的是 t1 的地址, t2.M2() 的修改可以影响到 t1 了。

如果声明一个方法 func f(t Intf) , 参数的传递和上面的直接赋值是一样的情况。

嵌套类型

声明一个类型 S,将 T 嵌入进去

type S struct {     T }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

使用下面的例子测试一下:

    t1 := T{"t1"}     s := S{t1}      fmt.Println("M1调用前:", s.Name)     s.M1()     fmt.Println("M1调用后:", s.Name)      fmt.Println("M2调用前:", s.Name)     s.M2()     fmt.Println("M2调用后:", s.Name)      fmt.Println(t1.Name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

输出:

M1调用前: t1 M1调用后: t1 M2调用前: t1 M2调用后: name2 t1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

将 T 嵌入 S, 那么 T 拥有的方法和属性 S 也是拥有的,但是接收者却不是 S 而是 T。

所以 s.M1() 相当于 M1(t1) 而不是 M1(s)

最后 t1 的值没有改变,因为我们嵌入的是 T 类型,所以 S{t1} 的时候是将 t1 拷贝了一份。

假如我们将 s 赋值给 Intf 接口会怎么样呢?

    var intf Intf = s     intf.M1()     intf.M2()
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

报错:

cannot use s (type S) as type Intf in assignment:     S does not implement Intf (M2 method has pointer receiver)
  • 1
  • 2
  • 1
  • 2

还是 M2() 的问题,因为 s 此时还是值类型。

var intf Intf = &s 这样的话编译通过了,如果在 intf.M2() 中改变了 Name 的值, s.Name 被改变了,但是 t1.Name 依然没变,因为现在 t1 和 s 已经没有联系了。


下面嵌入 *T 试试:

type S struct {     *T }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

使用时这样:

    t1 := T{"t1"}     s := S{&t1}      fmt.Println("M1调用前:", s.Name)     s.M1()     fmt.Println("M1调用后:", s.Name)      fmt.Println("M2调用前:", s.Name)     s.M2()     fmt.Println("M2调用后:", s.Name)      fmt.Println(t1.Name)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

惟一的区别是最后 t1 的值变了,因为我们复制的是指针。

接着赋值给接口试试:

    var intf Intf = s     intf.M1()     intf.M2()     fmt.Println(s.Name)
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

编译没有报错。这里我们传递给 intf 的是值类型而不是指针,为什么可以通过呢?

拷贝 s 的时候里面的 T 是指针类型,所以调用 M2() 的时候传递进去的是一个指针。

var intf Intf = &s 的效果和上面一样。

go中方法的接收者是值或者指针的区别的更多相关文章

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

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

  2. JAVA 类中方法参数与返回值

    无参无返回值的方法,用public void 方法名,来声明: 有参无返回值的方法,用public void 方法名,来声明: 有参有返回值的方法,用public int 方法名(int i,int ...

  3. 深入理解Java中方法的参数传递机制

    形参和实参 我们知道,在Java中定义方法时,是可以定义参数的,比如: public static void main(String[] args){ } 这里的args就是一个字符串数组类型的参数. ...

  4. golang指针接收者和值接收者方法调用笔记

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

  5. 慕课网-Java入门第一季-7-3 Java 中无参带返回值方法的使用

    来源:http://www.imooc.com/code/1579 如果方法不包含参数,但有返回值,我们称为无参带返回值的方法. 例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值 ...

  6. JSF页面中使用js函数回调后台bean方法并获取返回值的方法

    由于primefaces在国内使用的并不是太多,因此,国内对jsf做系统.详细的介绍的资料很少,即使有一些资料,也仅仅是对国外资料的简单翻译或者是仅仅讲表面现象(皮毛而已),它们的语句甚至还是错误的, ...

  7. java中方法的参数传递机制(值传递还是引用传递)

    看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?  答:是值传递.Java 编程语言只有值传递参 ...

  8. Java 中无参带返回值方法的使用

    如果方法不包含参数,但有返回值,我们称为无参带返回值的方法. 例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值为 int 类型的方法,执行的操作为计算两数之和,并返回结果 在 c ...

  9. treeview自动从表中添加标题和列值做目录的方法2

    treeview自动从表中添加标题和列值做目录的方法2,该方法是借鉴万一老师的 http://www.cnblogs.com/del/archive/2008/05/15/1114450.html 首 ...

随机推荐

  1. hdu 2073

    ps:感觉纯数学题.....我的思路就是找规律,发现dp(x+y) 就是 (0,x+y)的值,然后再加上x*根号2.就是他的距离.然后两个距离相减的绝对值就是两个点之间的距离了. 代码: #inclu ...

  2. (spring-第17回【AOP基础篇】) 创建增强类

    一.   增强类包含的信息: a)   横切逻辑(插入的具体代码) b)   部分连接点信息(在方法的哪个位置插入代码,比如方法前.方法后等). 二.   增强的类型 每一种增强有一个需要实现的增强类 ...

  3. 一些常用的vim设置

    以下内容皆来源于网络,感谢原作者.如果引用出处错误,请告知以便修改. 1. vim的几种模式和按键映射 转载自:[1] Map是Vim强大的一个重要原因,可以自定义各种快捷键,用起来自然得心应手.vi ...

  4. 监听调试web service的好工具TCPMon

    监听调试web service的好工具TCPMonhttp://ws.apache.org/commons/tcpmon/download.cgi TCPMon Tutorial Content In ...

  5. cocos2dx常见的46中+22中动作详解

    cocos2dx常见的46中+22中动作详解 分类: iOS2013-10-16 00:44 1429人阅读 评论(0) 收藏 举报 bool HelloWorld::init(){    ///// ...

  6. jq 一些小方法

    js 控制a标签的onclick方法 document.getElementById("a3").onclick = ""; window.document.g ...

  7. Bowtie2

    如何使用Bowtie2 相似功能的有: 创建索引 创建索引bowtie2-build使用的命令. -f指定要索引文件后,再给予索引的名称.名称可以连接到任何. bowtie2 build-f refe ...

  8. JS特殊函数(Function()构造函数、函数直接量)区别介绍

    函数定义 函数是由这样的方式进行声明的:关键字 function.函数名.一组参数,以及置于括号中的待执行代码. 函数的构造语法有这三种: 1.function functionName(arg0, ...

  9. 【转】Python 中map、reduce、filter函数

    转自:http://www.blogjava.net/vagasnail/articles/301140.html?opt=admin 介绍下Python 中 map,reduce,和filter 内 ...

  10. Knockoutjs实例 - 属性绑定(Bindings)之流程控制(Control flow)

    一.foreach binding 使用此功能可以方便我们循环遍历输出某个数组.集合中的内容. (1).循环遍历输出数组 View Row Code 1 <script type="t ...