本网站(662p.com)打包出售,且带程序代码数据,662p.com域名,程序内核采用TP框架开发,需要联系扣扣:2360248666 /wx:lianweikj
精品域名一口价出售:1y1m.com(350元) ,6b7b.com(400元) , 5k5j.com(380元) , yayj.com(1800元), jiongzhun.com(1000元) , niuzen.com(2800元) , zennei.com(5000元)
需要联系扣扣:2360248666 /wx:lianweikj
Go 的方法集与接口断言
liujianhua · 303浏览 · 发布于2021-04-27 +关注

方法集

引子

首先来看一段代码:

package main

import "fmt"

func main() {
	var v IpmHelloByValue
	CallSayHello(v)  // Ok,Output: Hello,I'm value
	CallSayHello(&v) // Ok,Output: Hello,I'm value
	var p IpmHelloByPointer
	CallSayHello(p)  // Not Ok,compile failed: IpmHelloByPointer does not implement IHello (SayHello method has pointer receiver)
	CallSayHello(&p) // OK, Output: Hello,I'm pointer
}

type IHello interface {
	SayHello()
}

type IpmHelloByPointer struct {
}

func (p *IpmHelloByPointer) SayHello() {
	fmt.Println("Hello,I'm pointer")
}

type IpmHelloByValue struct {
}

func (v IpmHelloByValue) SayHello() {
	fmt.Println("Hello,I'm value")
}

func CallSayHello(h IHello) {
	h.SayHello()
}

为何 CallSayHello(p)会编译失败,这就涉及到方法集了。

介绍

[方法集(method set)][https://golang.org/ref/spec#Method_sets]:定义了一组关联到给定类型的值或者指针的方法。在定义方法时所使用的接收者(receiver)的类型(值/指针),决定了该方法是关联到值还是关联到指针。

Method sets

A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing embedded fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.

The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.

类型的方法集决定了该类型所实现的接口,以及当使用该类型作为 receiver 时,所能调用的

ValuesMethods Receivers
T(t T)
*T(t T) and (t *T)

即:

  • T类型的,只能调用接收者类型的方法。

  • 指向T类型的指针,既能调用接收者类型指针的方法,也能调用接收者类型的方法。

例子

举个例子,为一个结构体声明两个方法,其中一个方法的 receiver 是 value,另一个方法的 receiver 是 pointer。

type MyStruct struct {
}

// receiver 是一个 value
func (m MyStruct) ValueReceiver() {
	fmt.Println("ValueReceiver")
}

// receiver 是一个 pointer
func (m *MyStruct) PointerReceiver() {
	fmt.Println("PointerReceiver")
}

为这个 struct 创建两个示例,一个的类型是 value,另一个的类型是 pointer。

func main() {
	var m MyStruct   // 方法集中只有 ValueReceiver()
	var pm *MyStruct // 方法集中既有 PointerReceiver(), 也有 ValueReceiver()
}

接下来创建两个 interface 以及使用这两个 interface 的函数

type IValue interface {
	ValueReceiver()
}

type IPointer interface {
	PointerReceiver()
}

func CallValue(v IValue) {
	v.ValueReceiver()
}

func CallPointer(p IPointer) {
	p.PointerReceiver()
}

分别将 m 和 pm 传入这两个函数会发生什么?

func main() {
	var m MyStruct   // 方法集中只有 ValueReceiver()
	var pm *MyStruct // 方法集中既有 PointerReceiver(), 也有 ValueReceiver()
	CallValue(m)	// OK
	// 因为 m 的方法集中并没有 PointerReceiver(),所以编译器说它没有实现 IPointer 接口
	CallPointer(m)	// Compile failed:Type does not implement 'IPointer' as 'PointerReceiver' method has a pointer receiver
	CallValue(pm)	// OK
	CallPointer(pm)	// OK
}

一个例外?

package main

import "fmt"

func main() {
	var m MyStruct
	m.ValueReceiver()   // OK,Output: ValueReceiver
    m.PointerReceiver() // OK,Output: PointerReceiver 这里为什么可以调用 PointerReceiver()?
	pm := &m
	pm.ValueReceiver()   // OK,Output: ValueReceiver
	pm.PointerReceiver() // OK,Output: PointerReceiver
}

type MyStruct struct {
}

// receiver 是一个 value
func (m MyStruct) ValueReceiver() {
	fmt.Println("ValueReceiver")
}

// receiver 是一个 pointer
func (m *MyStruct) PointerReceiver() {
	fmt.Println("PointerReceiver")
}

重点在第8行,按照之前所说的,m的方法集中并没有PointerReceiver()这个方法,为何这段代码可以编译成功?

这是因为编译器在后面做了工作。

m.PointerReceiver()

这句代码中,编译器对它做了一个隐式的 dereference 操作,偷偷的将它变成了

(&m).PointerReceiver()

所以最终还是通过一个 pointer 作为 receiver 去调用的 PointerReceiver

但是当变量无法取得地址时,编译器就无能为力了,比如这种:

MyStruct{}.ValueReceiver()      // OK
MyStruct{}.PointerReceiver()    // Not OK
(&MyStruct{}).PointerReceiver() // OK

因为编译器无法取得一个临时变量的地址。

接口断言

简介

接口断言可以判断一个 struct 是否实现了某个接口

通过

// 注意 _ 和 interfaceName 之间不要有 ','
var _ interfaceName = ImplementType

可以实现编译期的接口断言。

其中ImplementType既可以是一个 value,也可以是一个 pointer,如果是 value 类型,需要用 nil 来初始化。

例子

还是之前的例子:

type MyStruct struct {
}

// receiver 是一个 value
func (m MyStruct) ValueReceiver() {
	fmt.Println("ValueReceiver")
}

// receiver 是一个 pointer
func (m *MyStruct) PointerReceiver() {
	fmt.Println("PointerReceiver")
}

type IValue interface {
	ValueReceiver()
}

type IPointer interface {
	PointerReceiver()
}

加上接口断言:

var _ IValue = (*MyStruct)(nil)   // OK
var _ IPointer = (*MyStruct)(nil) // OK
var _ IValue = MyStruct{}         // OK
var _ IPointer = MyStruct{}       // Not OK: Type does not implement 'IPointer'

我是笨比

看起来这个接口断言好高大上呀,仔细一琢磨,它的形式不就是 go 中声明变量的方式么?

var 变量名字 类型 = 表达式

这儿只不过是把变量名字用 _ 代替了而已,意思是告诉编译器我不在乎这个变量的值。

总结:我是笨比(是什么迷惑住了我的双眼?)


相关推荐

PHP实现部分字符隐藏

沙雕mars · 1325浏览 · 2019-04-28 09:47:56
Java中ArrayList和LinkedList区别

kenrry1992 · 908浏览 · 2019-05-08 21:14:54
Tomcat 下载及安装配置

manongba · 970浏览 · 2019-05-13 21:03:56
JAVA变量介绍

manongba · 962浏览 · 2019-05-13 21:05:52
什么是SpringBoot

iamitnan · 1086浏览 · 2019-05-14 22:20:36
加载中

0评论

评论
分类专栏
小鸟云服务器
扫码进入手机网页