Golang中类型的方法集如何决定其实现的接口及可调用的方法

Golang中类型的方法集如何决定其实现的接口及可调用的方法 在 sync 包中我们有 waitgroup

Waitgroup 有函数 Wait()、done()、Add()。 所有这些函数的接收器类型都是指针 (wg *waitgroup)

但下面的代码可以正常工作

package main

import (
	"fmt"
	"runtime"
	"sync"
)

var wg sync.WaitGroup

func main() {
	fmt.Println("OS\t\t", runtime.GOOS)
	fmt.Println("ARCH\t\t", runtime.GOARCH)
	fmt.Println("CPUs\t\t", runtime.NumCPU())
	fmt.Println("Goroutines\t", runtime.NumGoroutine())

	wg.Add(1) // 这怎么可能(wg 变量不是指针类型)
	go foo()
	bar()

	fmt.Println("CPUs\t\t", runtime.NumCPU())
	fmt.Println("Goroutines\t", runtime.NumGoroutine())
	wg.Wait()
}

func foo() {
	for i := 0; i < 10; i++ {
		fmt.Println("foo:", i)
	}
	wg.Done()
}

我是 Go 语言的新手。请帮我理解这个概念


更多关于Golang中类型的方法集如何决定其实现的接口及可调用的方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我想我明白了…

它能够正常工作的原因是 wait()、done()、add() 都是 WaitGroup 类型的"方法集"…因此存在于该"方法集"中的这些方法可以通过该类型的接收器来调用

更多关于Golang中类型的方法集如何决定其实现的接口及可调用的方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是因为编译器知道 Add 方法需要一个指向 WaitGroup 的指针,并隐式地将函数调用从 wg.Add(1) "转换"为 (&wg).Add(1)

类似地,当你拥有一个值的指针并调用接收器为值类型的函数时,编译器会生成一个存根函数,将指针指向的值复制到临时变量中,以便对该值调用值函数。

在Go语言中,方法集的概念决定了类型实现接口的能力以及方法调用的方式。对于你的代码示例,关键在于理解值类型和指针类型在方法集上的差异。

根据Go语言规范:

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

在你的代码中,sync.WaitGroup的方法Add()Done()Wait()的接收器都是指针类型(*WaitGroup)。但是当你使用wg.Add(1)时,编译器会自动进行地址解引用,相当于(&wg).Add(1)

这里是一个更清晰的示例来说明这个概念:

package main

import (
    "fmt"
    "sync"
)

type MyType struct {
    value int
}

// 指针接收器方法
func (m *MyType) PointerMethod() {
    fmt.Printf("PointerMethod called, value: %d\n", m.value)
}

// 值接收器方法  
func (m MyType) ValueMethod() {
    fmt.Printf("ValueMethod called, value: %d\n", m.value)
}

func main() {
    var wg sync.WaitGroup
    // 这些调用都是有效的,编译器会自动处理
    wg.Add(1)  // 相当于 (&wg).Add(1)
    wg.Done()  // 相当于 (&wg).Done()
    wg.Wait()  // 相当于 (&wg).Wait()
    
    // 另一个示例
    var mt MyType
    mt.value = 10
    
    // 值类型调用指针接收器方法 - 有效
    mt.PointerMethod()  // 相当于 (&mt).PointerMethod()
    
    // 值类型调用值接收器方法 - 有效
    mt.ValueMethod()
    
    // 指针类型调用值接收器方法 - 也有效
    ptr := &MyType{value: 20}
    ptr.ValueMethod()   // 相当于 (*ptr).ValueMethod()
    ptr.PointerMethod()
}

在接口实现方面,区别更加明显:

package main

import "fmt"

type Interface interface {
    Method()
}

type MyStruct struct{}

func (m MyStruct) Method() {
    fmt.Println("Value receiver method")
}

func (m *MyStruct) PointerMethod() {
    fmt.Println("Pointer receiver method")
}

func main() {
    var s MyStruct
    var ptr *MyStruct = &MyStruct{}
    
    // 值类型可以调用值接收器方法
    s.Method()
    
    // 指针类型可以调用值接收器方法和指针接收器方法
    ptr.Method()       // 有效
    ptr.PointerMethod() // 有效
    
    // 但接口实现有区别
    var i Interface
    
    i = s    // 有效 - 值类型实现了接口
    i = ptr  // 也有效 - 指针类型也实现了接口
    
    i.Method()
}

在你的sync.WaitGroup例子中,虽然方法需要指针接收器,但当你使用值类型的wg变量调用这些方法时,Go编译器会自动获取变量的地址,使得代码能够正常工作。这种自动转换让代码更加简洁,同时保持了类型安全。

回到顶部