返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

6.2 方法集

发布于 2024-10-12 19:15:48 字数 2653 浏览 0 评论 0 收藏 0

类型有个与之相关的方法集合(method set),这决定了它是否实现某个接口。

根据接收参数(receiver)的不同,可分为 T*T 两种视角。

  • T.set = T
  • *T.set = T + *T
type T int

func  (T) A() {}   // 导出成员,否则反射无法获取。
func  (T) B() {}
func (*T) C() {}
func (*T) D() {}

// -----------------------------

func show(i interface{}) {
	t := reflect.TypeOf(i)
	for i := 0; i < t.NumMethod(); i++ {
		println(t.Method(i).Name)
	}	
}

func main() {
	var n T = 1
	var p *T = &n

	show(n)         //  T = [A, B]
	println("---")
	show(p)         // *T = [A, B, C, D]
}

直接方法调用,不涉及方法集。编译器自动转换所需参数(receiver)。

而转换(赋值)接口(interface)时,须检查方法集是否完全实现接口声明。

type Xer interface {
	B()
	C()
}

// -----------------------------

func main() {
	var n T = 1

    // 方法调用:不涉及方法集。
	n.B()         
	n.C()

    // 接口:检查方法集。
    
	// var x Xer = n
	//     ~ T does not implement Xer (C method has pointer receiver)

	var x Xer = &n
	x.B()
	x.C()
}

首先,接口会复制对象,且复制品 不能寻址 (unaddressable)。

T 实现接口,透过接口调用时, receiver 可被复制,却不能获取指针( &T )。

相反, *T 实现接口,目标对象在接口以外,无论是取值还是复制指针都没问题。

这就是方法集与接口相关,且 T = T*T = T + *T 的原因。

除直属方法外,列表里还包括匿名类型( E )的方法。

  • T{ E } = T + E
  • T{ *E } = T + E + *E
  • *T{ E | *E } = T + *T + E + *E
type E int

func  (E) V() {}
func (*E) P() {}

// -----------------------------

func show(i interface{}) {
	t := reflect.TypeOf(i)
	for i := 0; i < t.NumMethod(); i++ {
		println("  ", t.Method(i).Name)
	}	
}

func main() {
	println("T{ E }")
	show(struct{
		E
	}{})

	println("T{ *E }")
	show(struct{
		*E
	}{})

	println("*T{ E }")
	show(&struct{
		E
	}{})

	println("*T{ *E }")
	show(&struct{
		*E
	}{})
}

//  T{  E }: V
//  T{ *E }: P, V
// *T{  E }: P, V
// *T{ *E }: P, V

别名扩展

通过类型别名,对方法集进行分类,更便于维护。或新增别名,为类型添加扩展方法。

type X int
func (*X) A() { println("X.A") }

type Y = X                           // 别名
func (*Y) B() { println("Y.B") }     // 扩展方法

func main() {
	var x X
	x.A()
	x.B()

	var y Y
	y.A()
	y.B()
}

通过反射,可以看到 “扩展” 被合并的效果。

func main() {
	var n X
	t := reflect.TypeOf(&n)

	for i := 0; i < t.NumMethod(); i++ {
		fmt.Println(t.Method(i))
	}
}

// A: func(*main.X)
// B: func(*main.X)

需要注意,不同包的类型可定义别名,但不能定义方法。

type X = bytes.Buffer

// func (*X) B() { println("X.b") } 
//       ~~ cannot define new methods on non-local type

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文