Golang中*T类型能否拥有T的方法?

Golang中*T类型能否拥有T的方法? 方法 World 属于 *X,但我可以在一个 X 实例上调用它,这是设计如此吗?那么 T 和 *T 的方法集之间是什么关系?它们完全相同吗?非常感谢。

type X struct {}
func (x X) Hello() {
}
func (x *X) World() {
}

func main() {
  a := X{}
  a.World()
}
6 回复

哦,我明白你的意思了。我搞混了!看起来Dean的回答解决了你的问题!

更多关于Golang中*T类型能否拥有T的方法?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


谢谢。明白了:通过这种设计,我们有两种方法:按值和按引用。很好!

是的,这是有意为之的设计;以下是语言规范中的相关说明:The Go Programming Language Specification - go.dev

skillian:

Go 编程语言规范 - go.dev

看起来这段话的意思是:*T 的方法集包含了 T 的方法集。但在我的例子中,我在一个 X 实例上调用了 *X 的方法。这应该会失败:World 不是 X 的方法。

简而言之,编译器会自动将 a.World() 转换为 (&a).World()。根据规范Calls 部分稍后的说明:

如果 x 的(类型的)方法集包含 m,并且参数列表可以赋值给 m 的参数列表,则方法调用 x.m() 是有效的。如果 x可寻址的并且 &x 的方法集包含 m,那么 x.m()(&x).m() 的简写:

StackOverflow上的这个问题有一个很好的观点,指出了两者之间的细微差别以及你可能选择其中一个的原因:

以指针作为接收器的方法能够修改该接收器的值,而以值作为接收器的方法则不能。

该问题中包含大量有用的信息。在处理使用反射的东西时,例如MarshalJSON,我也要小心。在这种情况下,你使用哪种接收器确实很重要

在Go语言中,*T类型的方法集包含T的方法集,但反过来不成立。这是Go语言设计中的隐式解引用特性。

具体来说:

  • T类型的方法集包含所有接收者为T的方法
  • *T类型的方法集包含所有接收者为T*T的方法

在你的代码中,World()方法的接收者是*X,但可以在X实例上调用,这是因为Go编译器会自动进行地址获取和隐式解引用。

示例代码:

package main

import "fmt"

type X struct {
    value int
}

// 方法属于 T
func (x X) Hello() {
    fmt.Println("Hello from", x.value)
}

// 方法属于 *T
func (x *X) World() {
    fmt.Println("World from", x.value)
}

// 另一个示例,展示方法集差异
type Y struct {
    data string
}

// 只有 *Y 有这个方法
func (y *Y) Modify() {
    y.data = "modified"
}

func main() {
    // 示例1:X类型
    a := X{value: 1}
    
    // 可以调用,编译器自动转换为 (&a).Hello()
    a.Hello()
    
    // 可以调用,编译器自动转换为 (&a).World()
    a.World()
    
    // 示例2:Y类型
    b := Y{data: "original"}
    
    // 这行会编译错误,因为Modify()只属于*Y
    // b.Modify() // 取消注释会报错
    
    // 正确调用方式
    (&b).Modify()
    fmt.Println(b.data) // 输出: modified
    
    // 或者使用指针变量
    c := &Y{data: "pointer"}
    c.Modify()
    fmt.Println(c.data) // 输出: modified
}

方法集关系总结:

  • T的方法集:所有接收者为T的方法
  • *T的方法集:所有接收者为T*T的方法

这种设计允许在需要修改接收者时使用指针接收者,同时在值类型上也能调用这些方法,提高了代码的灵活性。

回到顶部