go 的接口赋值是值传递嘛?

package main

import (
    "fmt"
)

type Man struct {
    name  string
    age   int
    phone string
}

//A Man method to say hi
func (h Man) SayHi() {
    fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}

//A Man can sing a song
func (h Man) Sing(lyrics string) {
    fmt.Println("La la la la...", lyrics)
}

// Interface Human is implemented by man
// because it contains methods implemented by them.
type Human interface {
    SayHi()
    Sing(lyrics string)
}

func main() {
    mike := Man{"Mike", 25, "222-222-XXX"}
    var human Human
    human = mike
    // 断言
    person := human.(Man)
    person.name = "salamander"

    fmt.Println(mike, person)
}

输出结果是:

{Mike 25 222-222-XXX} {salamander 25 222-222-XXX}

接口赋值是值传递?

阅读 8.6k
2 个回答

我觉得这个问题非常有意思,晚上也花了点时间研究一下。我觉得问题不在于接口赋值,而在于赋值
我简单地修改了一点代码,先看看这种情况:

func main() {
    mike := Man{"Mike", 25, "222-222-XXX"}
    fmt.Printf("mike's address is  %p \n",&mike)
    fake_mike := mike
    fmt.Printf("fake_mike's address is  %p \n",&fake_mike)
    fake_mike.name = "fake mike"
    fmt.Println(mike,fake_mike)
}

输出:

mike's address is  0xc420086030 
fake_mike's address is  0xc420086060 
{Mike 25 222-222-XXX} {fake mike 25 222-222-XXX}

我们再看看如果是java会得到什么样的结果:

 public class Man{
        String name;
        int age;
        String phone;
        public Man(String name,int age, String phone){
            this.name = name;
            this.age = age;
            this.phone = phone;
        }
    
    public static void main(){
        Man mike = new Man("mike",22,"222-222-XXX");
        Man fake_mike = mike;
        fake_mike.name = "fake_mike";
        System.out.println("{" + mike.name +" "+ mike.age + " " + mike.phone +"} , {"+fake_mike.name+" "+ mike.age + " " + mike.phone +"}");
        System.out.println("mike's hashCode is " + mike.hashCode());
        System.out.println("fake_mike's hashCode is " + fake_mike.hashCode());
    }
}

输出是这样的:

{fake_mike 22 222-222-XXX} , {fake_mike 22 222-222-XXX}
mike's hashCode is 497106867
fake_mike's hashCode is 497106867

可以看出,在java当中,对象赋值是按引用赋值的。但而在go中,结构体赋值应该是按值赋值的,为了说明这点,请往下看。
这个问题的关键在于:

var human Human
    human = mike

person := human.(Man)

中的mikehuman,person都不是同一个引用。
为了说明这点,下面的代码将输出每个变量的内存地址:


func main() {
    mike := Man{"Mike", 25, "222-222-XXX"}
    fmt.Printf("mike's address is  %p \n",&mike)
    fake_mike := mike
    fmt.Printf("fake_mike's address is  %p \n",&fake_mike)
    fake_mike.name = "fake mike"

    var human Human
    human = mike
    fmt.Printf("human's address is %p \n",&human)

    // 断言
    person := human.(Man)
    fmt.Printf("person's address is %p \n",&person)

    person.name = "salamander"
    fmt.Println(mike, person)
}

输出:

mike's address is  0xc4200760c0 
fake_mike's address is  0xc4200760f0 
human's address is 0xc420072190 
person's address is 0xc420076150 
{Mike 25 222-222-XXX} {salamander 25 222-222-XXX}

可见都是不同的内存地址。综上所述,不仅仅是接口赋值,普通的赋值也是按值赋值的。

能力有限,如果有错误请一定不吝指教,非常感谢!

  • 你代码里声明了3个变量:mikehumanperson。既然是3个不同变量,那么这3个变量的内存地址肯定不相同。

  • 变量mikepersonMan struct类型,变量humanHuman interface类型。human = mike语句,就是把mike这个struct值拷贝到了变量human

  • 假设实现Human interface是Man指针

func (h *Man) SayHi() {
}
func (h *Man) Sing(lyrics string) {  
}

func main(){
    mike := &Man{"Mike", 25, "222-222-XXX"}
    var human Human
    human = mike
}

那么,human = mike语句的含义就变成了拷贝指针值,也就是拷贝存储实际的Man struct的地址。

  • 所以说,我也认同你值传递或者值拷贝的说法。如果目标是struct,那么赋值操作实际就是拷贝整个struct到另一个变量。如果目标是指针,那么赋值操作就是拷贝指针值本身。如果是基本类型,那更加是拷贝值无疑了。

  • 一般在定义函数返回值时我会有类似思考,是返回struct,还是struct指针?如果返回struct,那就要发生一次struct的拷贝,代价较大。如果返回struct指针,那么仅会拷贝指针本身(暂不考虑是分配到堆/栈的问题),代价较小。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题